├── .DS_Store ├── Decoding_binORG.m ├── LICENSE ├── README.md ├── SVM_ECOC_ERP_Decoding_2020.m ├── SVM_ECOC_ERP_PermutationTesting_2020.m ├── SVM_ECOC_Stat_Plot_Permutation_2020.m ├── boundedline.m └── inpaint_nans.m /.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ammsimmons/binorg_decoding/89264203ebbd8ae7577c3524b1837621eb060aae/.DS_Store -------------------------------------------------------------------------------- /Decoding_binORG.m: -------------------------------------------------------------------------------- 1 | % binorgEEG script: 2 | % Use *right after* Extracting Bin-Based Epochs 3 | % Creates bin-organized .mat files for Decoding 4 | % Writes .mat files to current working directory 5 | 6 | % Place continuous BE datasets (.set & .fdt) in current working directory 7 | % Examples of these kind of data can be found on box: 8 | % https://ucdavis.box.com/s/jrd9zus448nkc0wjkjf1x5nxbc7oxwg8 9 | 10 | % by Aaron M. Simmons, University of California, Davis 11 | 12 | clear all; 13 | close all; 14 | 15 | 16 | parentfolder = pwd; 17 | subject_list = {505, 506, 507, 508, 509, 510, 512, 514, 516, 517, 519, 520, 521, 523, 524, 525}; 18 | numsubjects = length(subject_list); 19 | 20 | 21 | for s = 1:numsubjects 22 | subject = subject_list{s}; 23 | subjectfolder = [parentfolder]; %loc of file 24 | fprintf('\n\n\n*** Processing subject %d (%s) ***\n\n\n', s, num2str(subject)); 25 | 26 | %Initialize EEG 27 | eeglab; 28 | 29 | %load data 30 | EEG = pop_loadset('filename', [num2str(subject) '_binned_be.set'], 'filepath', subjectfolder); 31 | 32 | %use binorg-EEG function (this function is only on ERPlab V8.1) 33 | binepEEG_to_binorgEEG(EEG, ['Decoding_BE_' num2str(subject)]); 34 | % Produces bin-organized .mat file and outputs file into current working directory 35 | % it will be called "Decoding_BE_XXX" as specified in string filename 36 | 37 | close all; 38 | clear EEG; 39 | 40 | end 41 | 42 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | GNU GENERAL PUBLIC LICENSE 2 | Version 3, 29 June 2007 3 | 4 | Copyright (C) 2007 Free Software Foundation, Inc. 5 | Everyone is permitted to copy and distribute verbatim copies 6 | of this license document, but changing it is not allowed. 7 | 8 | Preamble 9 | 10 | The GNU General Public License is a free, copyleft license for 11 | software and other kinds of works. 12 | 13 | The licenses for most software and other practical works are designed 14 | to take away your freedom to share and change the works. By contrast, 15 | the GNU General Public License is intended to guarantee your freedom to 16 | share and change all versions of a program--to make sure it remains free 17 | software for all its users. We, the Free Software Foundation, use the 18 | GNU General Public License for most of our software; it applies also to 19 | any other work released this way by its authors. You can apply it to 20 | your programs, too. 21 | 22 | When we speak of free software, we are referring to freedom, not 23 | price. Our General Public Licenses are designed to make sure that you 24 | have the freedom to distribute copies of free software (and charge for 25 | them if you wish), that you receive source code or can get it if you 26 | want it, that you can change the software or use pieces of it in new 27 | free programs, and that you know you can do these things. 28 | 29 | To protect your rights, we need to prevent others from denying you 30 | these rights or asking you to surrender the rights. Therefore, you have 31 | certain responsibilities if you distribute copies of the software, or if 32 | you modify it: responsibilities to respect the freedom of others. 33 | 34 | For example, if you distribute copies of such a program, whether 35 | gratis or for a fee, you must pass on to the recipients the same 36 | freedoms that you received. You must make sure that they, too, receive 37 | or can get the source code. And you must show them these terms so they 38 | know their rights. 39 | 40 | Developers that use the GNU GPL protect your rights with two steps: 41 | (1) assert copyright on the software, and (2) offer you this License 42 | giving you legal permission to copy, distribute and/or modify it. 43 | 44 | For the developers' and authors' protection, the GPL clearly explains 45 | that there is no warranty for this free software. For both users' and 46 | authors' sake, the GPL requires that modified versions be marked as 47 | changed, so that their problems will not be attributed erroneously to 48 | authors of previous versions. 49 | 50 | Some devices are designed to deny users access to install or run 51 | modified versions of the software inside them, although the manufacturer 52 | can do so. This is fundamentally incompatible with the aim of 53 | protecting users' freedom to change the software. The systematic 54 | pattern of such abuse occurs in the area of products for individuals to 55 | use, which is precisely where it is most unacceptable. Therefore, we 56 | have designed this version of the GPL to prohibit the practice for those 57 | products. If such problems arise substantially in other domains, we 58 | stand ready to extend this provision to those domains in future versions 59 | of the GPL, as needed to protect the freedom of users. 60 | 61 | Finally, every program is threatened constantly by software patents. 62 | States should not allow patents to restrict development and use of 63 | software on general-purpose computers, but in those that do, we wish to 64 | avoid the special danger that patents applied to a free program could 65 | make it effectively proprietary. To prevent this, the GPL assures that 66 | patents cannot be used to render the program non-free. 67 | 68 | The precise terms and conditions for copying, distribution and 69 | modification follow. 70 | 71 | TERMS AND CONDITIONS 72 | 73 | 0. Definitions. 74 | 75 | "This License" refers to version 3 of the GNU General Public License. 76 | 77 | "Copyright" also means copyright-like laws that apply to other kinds of 78 | works, such as semiconductor masks. 79 | 80 | "The Program" refers to any copyrightable work licensed under this 81 | License. Each licensee is addressed as "you". "Licensees" and 82 | "recipients" may be individuals or organizations. 83 | 84 | To "modify" a work means to copy from or adapt all or part of the work 85 | in a fashion requiring copyright permission, other than the making of an 86 | exact copy. The resulting work is called a "modified version" of the 87 | earlier work or a work "based on" the earlier work. 88 | 89 | A "covered work" means either the unmodified Program or a work based 90 | on the Program. 91 | 92 | To "propagate" a work means to do anything with it that, without 93 | permission, would make you directly or secondarily liable for 94 | infringement under applicable copyright law, except executing it on a 95 | computer or modifying a private copy. Propagation includes copying, 96 | distribution (with or without modification), making available to the 97 | public, and in some countries other activities as well. 98 | 99 | To "convey" a work means any kind of propagation that enables other 100 | parties to make or receive copies. Mere interaction with a user through 101 | a computer network, with no transfer of a copy, is not conveying. 102 | 103 | An interactive user interface displays "Appropriate Legal Notices" 104 | to the extent that it includes a convenient and prominently visible 105 | feature that (1) displays an appropriate copyright notice, and (2) 106 | tells the user that there is no warranty for the work (except to the 107 | extent that warranties are provided), that licensees may convey the 108 | work under this License, and how to view a copy of this License. If 109 | the interface presents a list of user commands or options, such as a 110 | menu, a prominent item in the list meets this criterion. 111 | 112 | 1. Source Code. 113 | 114 | The "source code" for a work means the preferred form of the work 115 | for making modifications to it. "Object code" means any non-source 116 | form of a work. 117 | 118 | A "Standard Interface" means an interface that either is an official 119 | standard defined by a recognized standards body, or, in the case of 120 | interfaces specified for a particular programming language, one that 121 | is widely used among developers working in that language. 122 | 123 | The "System Libraries" of an executable work include anything, other 124 | than the work as a whole, that (a) is included in the normal form of 125 | packaging a Major Component, but which is not part of that Major 126 | Component, and (b) serves only to enable use of the work with that 127 | Major Component, or to implement a Standard Interface for which an 128 | implementation is available to the public in source code form. A 129 | "Major Component", in this context, means a major essential component 130 | (kernel, window system, and so on) of the specific operating system 131 | (if any) on which the executable work runs, or a compiler used to 132 | produce the work, or an object code interpreter used to run it. 133 | 134 | The "Corresponding Source" for a work in object code form means all 135 | the source code needed to generate, install, and (for an executable 136 | work) run the object code and to modify the work, including scripts to 137 | control those activities. However, it does not include the work's 138 | System Libraries, or general-purpose tools or generally available free 139 | programs which are used unmodified in performing those activities but 140 | which are not part of the work. For example, Corresponding Source 141 | includes interface definition files associated with source files for 142 | the work, and the source code for shared libraries and dynamically 143 | linked subprograms that the work is specifically designed to require, 144 | such as by intimate data communication or control flow between those 145 | subprograms and other parts of the work. 146 | 147 | The Corresponding Source need not include anything that users 148 | can regenerate automatically from other parts of the Corresponding 149 | Source. 150 | 151 | The Corresponding Source for a work in source code form is that 152 | same work. 153 | 154 | 2. Basic Permissions. 155 | 156 | All rights granted under this License are granted for the term of 157 | copyright on the Program, and are irrevocable provided the stated 158 | conditions are met. This License explicitly affirms your unlimited 159 | permission to run the unmodified Program. The output from running a 160 | covered work is covered by this License only if the output, given its 161 | content, constitutes a covered work. This License acknowledges your 162 | rights of fair use or other equivalent, as provided by copyright law. 163 | 164 | You may make, run and propagate covered works that you do not 165 | convey, without conditions so long as your license otherwise remains 166 | in force. You may convey covered works to others for the sole purpose 167 | of having them make modifications exclusively for you, or provide you 168 | with facilities for running those works, provided that you comply with 169 | the terms of this License in conveying all material for which you do 170 | not control copyright. Those thus making or running the covered works 171 | for you must do so exclusively on your behalf, under your direction 172 | and control, on terms that prohibit them from making any copies of 173 | your copyrighted material outside their relationship with you. 174 | 175 | Conveying under any other circumstances is permitted solely under 176 | the conditions stated below. Sublicensing is not allowed; section 10 177 | makes it unnecessary. 178 | 179 | 3. Protecting Users' Legal Rights From Anti-Circumvention Law. 180 | 181 | No covered work shall be deemed part of an effective technological 182 | measure under any applicable law fulfilling obligations under article 183 | 11 of the WIPO copyright treaty adopted on 20 December 1996, or 184 | similar laws prohibiting or restricting circumvention of such 185 | measures. 186 | 187 | When you convey a covered work, you waive any legal power to forbid 188 | circumvention of technological measures to the extent such circumvention 189 | is effected by exercising rights under this License with respect to 190 | the covered work, and you disclaim any intention to limit operation or 191 | modification of the work as a means of enforcing, against the work's 192 | users, your or third parties' legal rights to forbid circumvention of 193 | technological measures. 194 | 195 | 4. Conveying Verbatim Copies. 196 | 197 | You may convey verbatim copies of the Program's source code as you 198 | receive it, in any medium, provided that you conspicuously and 199 | appropriately publish on each copy an appropriate copyright notice; 200 | keep intact all notices stating that this License and any 201 | non-permissive terms added in accord with section 7 apply to the code; 202 | keep intact all notices of the absence of any warranty; and give all 203 | recipients a copy of this License along with the Program. 204 | 205 | You may charge any price or no price for each copy that you convey, 206 | and you may offer support or warranty protection for a fee. 207 | 208 | 5. Conveying Modified Source Versions. 209 | 210 | You may convey a work based on the Program, or the modifications to 211 | produce it from the Program, in the form of source code under the 212 | terms of section 4, provided that you also meet all of these conditions: 213 | 214 | a) The work must carry prominent notices stating that you modified 215 | it, and giving a relevant date. 216 | 217 | b) The work must carry prominent notices stating that it is 218 | released under this License and any conditions added under section 219 | 7. This requirement modifies the requirement in section 4 to 220 | "keep intact all notices". 221 | 222 | c) You must license the entire work, as a whole, under this 223 | License to anyone who comes into possession of a copy. This 224 | License will therefore apply, along with any applicable section 7 225 | additional terms, to the whole of the work, and all its parts, 226 | regardless of how they are packaged. This License gives no 227 | permission to license the work in any other way, but it does not 228 | invalidate such permission if you have separately received it. 229 | 230 | d) If the work has interactive user interfaces, each must display 231 | Appropriate Legal Notices; however, if the Program has interactive 232 | interfaces that do not display Appropriate Legal Notices, your 233 | work need not make them do so. 234 | 235 | A compilation of a covered work with other separate and independent 236 | works, which are not by their nature extensions of the covered work, 237 | and which are not combined with it such as to form a larger program, 238 | in or on a volume of a storage or distribution medium, is called an 239 | "aggregate" if the compilation and its resulting copyright are not 240 | used to limit the access or legal rights of the compilation's users 241 | beyond what the individual works permit. Inclusion of a covered work 242 | in an aggregate does not cause this License to apply to the other 243 | parts of the aggregate. 244 | 245 | 6. Conveying Non-Source Forms. 246 | 247 | You may convey a covered work in object code form under the terms 248 | of sections 4 and 5, provided that you also convey the 249 | machine-readable Corresponding Source under the terms of this License, 250 | in one of these ways: 251 | 252 | a) Convey the object code in, or embodied in, a physical product 253 | (including a physical distribution medium), accompanied by the 254 | Corresponding Source fixed on a durable physical medium 255 | customarily used for software interchange. 256 | 257 | b) Convey the object code in, or embodied in, a physical product 258 | (including a physical distribution medium), accompanied by a 259 | written offer, valid for at least three years and valid for as 260 | long as you offer spare parts or customer support for that product 261 | model, to give anyone who possesses the object code either (1) a 262 | copy of the Corresponding Source for all the software in the 263 | product that is covered by this License, on a durable physical 264 | medium customarily used for software interchange, for a price no 265 | more than your reasonable cost of physically performing this 266 | conveying of source, or (2) access to copy the 267 | Corresponding Source from a network server at no charge. 268 | 269 | c) Convey individual copies of the object code with a copy of the 270 | written offer to provide the Corresponding Source. This 271 | alternative is allowed only occasionally and noncommercially, and 272 | only if you received the object code with such an offer, in accord 273 | with subsection 6b. 274 | 275 | d) Convey the object code by offering access from a designated 276 | place (gratis or for a charge), and offer equivalent access to the 277 | Corresponding Source in the same way through the same place at no 278 | further charge. You need not require recipients to copy the 279 | Corresponding Source along with the object code. If the place to 280 | copy the object code is a network server, the Corresponding Source 281 | may be on a different server (operated by you or a third party) 282 | that supports equivalent copying facilities, provided you maintain 283 | clear directions next to the object code saying where to find the 284 | Corresponding Source. Regardless of what server hosts the 285 | Corresponding Source, you remain obligated to ensure that it is 286 | available for as long as needed to satisfy these requirements. 287 | 288 | e) Convey the object code using peer-to-peer transmission, provided 289 | you inform other peers where the object code and Corresponding 290 | Source of the work are being offered to the general public at no 291 | charge under subsection 6d. 292 | 293 | A separable portion of the object code, whose source code is excluded 294 | from the Corresponding Source as a System Library, need not be 295 | included in conveying the object code work. 296 | 297 | A "User Product" is either (1) a "consumer product", which means any 298 | tangible personal property which is normally used for personal, family, 299 | or household purposes, or (2) anything designed or sold for incorporation 300 | into a dwelling. In determining whether a product is a consumer product, 301 | doubtful cases shall be resolved in favor of coverage. For a particular 302 | product received by a particular user, "normally used" refers to a 303 | typical or common use of that class of product, regardless of the status 304 | of the particular user or of the way in which the particular user 305 | actually uses, or expects or is expected to use, the product. A product 306 | is a consumer product regardless of whether the product has substantial 307 | commercial, industrial or non-consumer uses, unless such uses represent 308 | the only significant mode of use of the product. 309 | 310 | "Installation Information" for a User Product means any methods, 311 | procedures, authorization keys, or other information required to install 312 | and execute modified versions of a covered work in that User Product from 313 | a modified version of its Corresponding Source. The information must 314 | suffice to ensure that the continued functioning of the modified object 315 | code is in no case prevented or interfered with solely because 316 | modification has been made. 317 | 318 | If you convey an object code work under this section in, or with, or 319 | specifically for use in, a User Product, and the conveying occurs as 320 | part of a transaction in which the right of possession and use of the 321 | User Product is transferred to the recipient in perpetuity or for a 322 | fixed term (regardless of how the transaction is characterized), the 323 | Corresponding Source conveyed under this section must be accompanied 324 | by the Installation Information. But this requirement does not apply 325 | if neither you nor any third party retains the ability to install 326 | modified object code on the User Product (for example, the work has 327 | been installed in ROM). 328 | 329 | The requirement to provide Installation Information does not include a 330 | requirement to continue to provide support service, warranty, or updates 331 | for a work that has been modified or installed by the recipient, or for 332 | the User Product in which it has been modified or installed. Access to a 333 | network may be denied when the modification itself materially and 334 | adversely affects the operation of the network or violates the rules and 335 | protocols for communication across the network. 336 | 337 | Corresponding Source conveyed, and Installation Information provided, 338 | in accord with this section must be in a format that is publicly 339 | documented (and with an implementation available to the public in 340 | source code form), and must require no special password or key for 341 | unpacking, reading or copying. 342 | 343 | 7. Additional Terms. 344 | 345 | "Additional permissions" are terms that supplement the terms of this 346 | License by making exceptions from one or more of its conditions. 347 | Additional permissions that are applicable to the entire Program shall 348 | be treated as though they were included in this License, to the extent 349 | that they are valid under applicable law. If additional permissions 350 | apply only to part of the Program, that part may be used separately 351 | under those permissions, but the entire Program remains governed by 352 | this License without regard to the additional permissions. 353 | 354 | When you convey a copy of a covered work, you may at your option 355 | remove any additional permissions from that copy, or from any part of 356 | it. (Additional permissions may be written to require their own 357 | removal in certain cases when you modify the work.) You may place 358 | additional permissions on material, added by you to a covered work, 359 | for which you have or can give appropriate copyright permission. 360 | 361 | Notwithstanding any other provision of this License, for material you 362 | add to a covered work, you may (if authorized by the copyright holders of 363 | that material) supplement the terms of this License with terms: 364 | 365 | a) Disclaiming warranty or limiting liability differently from the 366 | terms of sections 15 and 16 of this License; or 367 | 368 | b) Requiring preservation of specified reasonable legal notices or 369 | author attributions in that material or in the Appropriate Legal 370 | Notices displayed by works containing it; or 371 | 372 | c) Prohibiting misrepresentation of the origin of that material, or 373 | requiring that modified versions of such material be marked in 374 | reasonable ways as different from the original version; or 375 | 376 | d) Limiting the use for publicity purposes of names of licensors or 377 | authors of the material; or 378 | 379 | e) Declining to grant rights under trademark law for use of some 380 | trade names, trademarks, or service marks; or 381 | 382 | f) Requiring indemnification of licensors and authors of that 383 | material by anyone who conveys the material (or modified versions of 384 | it) with contractual assumptions of liability to the recipient, for 385 | any liability that these contractual assumptions directly impose on 386 | those licensors and authors. 387 | 388 | All other non-permissive additional terms are considered "further 389 | restrictions" within the meaning of section 10. If the Program as you 390 | received it, or any part of it, contains a notice stating that it is 391 | governed by this License along with a term that is a further 392 | restriction, you may remove that term. If a license document contains 393 | a further restriction but permits relicensing or conveying under this 394 | License, you may add to a covered work material governed by the terms 395 | of that license document, provided that the further restriction does 396 | not survive such relicensing or conveying. 397 | 398 | If you add terms to a covered work in accord with this section, you 399 | must place, in the relevant source files, a statement of the 400 | additional terms that apply to those files, or a notice indicating 401 | where to find the applicable terms. 402 | 403 | Additional terms, permissive or non-permissive, may be stated in the 404 | form of a separately written license, or stated as exceptions; 405 | the above requirements apply either way. 406 | 407 | 8. Termination. 408 | 409 | You may not propagate or modify a covered work except as expressly 410 | provided under this License. Any attempt otherwise to propagate or 411 | modify it is void, and will automatically terminate your rights under 412 | this License (including any patent licenses granted under the third 413 | paragraph of section 11). 414 | 415 | However, if you cease all violation of this License, then your 416 | license from a particular copyright holder is reinstated (a) 417 | provisionally, unless and until the copyright holder explicitly and 418 | finally terminates your license, and (b) permanently, if the copyright 419 | holder fails to notify you of the violation by some reasonable means 420 | prior to 60 days after the cessation. 421 | 422 | Moreover, your license from a particular copyright holder is 423 | reinstated permanently if the copyright holder notifies you of the 424 | violation by some reasonable means, this is the first time you have 425 | received notice of violation of this License (for any work) from that 426 | copyright holder, and you cure the violation prior to 30 days after 427 | your receipt of the notice. 428 | 429 | Termination of your rights under this section does not terminate the 430 | licenses of parties who have received copies or rights from you under 431 | this License. If your rights have been terminated and not permanently 432 | reinstated, you do not qualify to receive new licenses for the same 433 | material under section 10. 434 | 435 | 9. Acceptance Not Required for Having Copies. 436 | 437 | You are not required to accept this License in order to receive or 438 | run a copy of the Program. Ancillary propagation of a covered work 439 | occurring solely as a consequence of using peer-to-peer transmission 440 | to receive a copy likewise does not require acceptance. However, 441 | nothing other than this License grants you permission to propagate or 442 | modify any covered work. These actions infringe copyright if you do 443 | not accept this License. Therefore, by modifying or propagating a 444 | covered work, you indicate your acceptance of this License to do so. 445 | 446 | 10. Automatic Licensing of Downstream Recipients. 447 | 448 | Each time you convey a covered work, the recipient automatically 449 | receives a license from the original licensors, to run, modify and 450 | propagate that work, subject to this License. You are not responsible 451 | for enforcing compliance by third parties with this License. 452 | 453 | An "entity transaction" is a transaction transferring control of an 454 | organization, or substantially all assets of one, or subdividing an 455 | organization, or merging organizations. If propagation of a covered 456 | work results from an entity transaction, each party to that 457 | transaction who receives a copy of the work also receives whatever 458 | licenses to the work the party's predecessor in interest had or could 459 | give under the previous paragraph, plus a right to possession of the 460 | Corresponding Source of the work from the predecessor in interest, if 461 | the predecessor has it or can get it with reasonable efforts. 462 | 463 | You may not impose any further restrictions on the exercise of the 464 | rights granted or affirmed under this License. For example, you may 465 | not impose a license fee, royalty, or other charge for exercise of 466 | rights granted under this License, and you may not initiate litigation 467 | (including a cross-claim or counterclaim in a lawsuit) alleging that 468 | any patent claim is infringed by making, using, selling, offering for 469 | sale, or importing the Program or any portion of it. 470 | 471 | 11. Patents. 472 | 473 | A "contributor" is a copyright holder who authorizes use under this 474 | License of the Program or a work on which the Program is based. The 475 | work thus licensed is called the contributor's "contributor version". 476 | 477 | A contributor's "essential patent claims" are all patent claims 478 | owned or controlled by the contributor, whether already acquired or 479 | hereafter acquired, that would be infringed by some manner, permitted 480 | by this License, of making, using, or selling its contributor version, 481 | but do not include claims that would be infringed only as a 482 | consequence of further modification of the contributor version. For 483 | purposes of this definition, "control" includes the right to grant 484 | patent sublicenses in a manner consistent with the requirements of 485 | this License. 486 | 487 | Each contributor grants you a non-exclusive, worldwide, royalty-free 488 | patent license under the contributor's essential patent claims, to 489 | make, use, sell, offer for sale, import and otherwise run, modify and 490 | propagate the contents of its contributor version. 491 | 492 | In the following three paragraphs, a "patent license" is any express 493 | agreement or commitment, however denominated, not to enforce a patent 494 | (such as an express permission to practice a patent or covenant not to 495 | sue for patent infringement). To "grant" such a patent license to a 496 | party means to make such an agreement or commitment not to enforce a 497 | patent against the party. 498 | 499 | If you convey a covered work, knowingly relying on a patent license, 500 | and the Corresponding Source of the work is not available for anyone 501 | to copy, free of charge and under the terms of this License, through a 502 | publicly available network server or other readily accessible means, 503 | then you must either (1) cause the Corresponding Source to be so 504 | available, or (2) arrange to deprive yourself of the benefit of the 505 | patent license for this particular work, or (3) arrange, in a manner 506 | consistent with the requirements of this License, to extend the patent 507 | license to downstream recipients. "Knowingly relying" means you have 508 | actual knowledge that, but for the patent license, your conveying the 509 | covered work in a country, or your recipient's use of the covered work 510 | in a country, would infringe one or more identifiable patents in that 511 | country that you have reason to believe are valid. 512 | 513 | If, pursuant to or in connection with a single transaction or 514 | arrangement, you convey, or propagate by procuring conveyance of, a 515 | covered work, and grant a patent license to some of the parties 516 | receiving the covered work authorizing them to use, propagate, modify 517 | or convey a specific copy of the covered work, then the patent license 518 | you grant is automatically extended to all recipients of the covered 519 | work and works based on it. 520 | 521 | A patent license is "discriminatory" if it does not include within 522 | the scope of its coverage, prohibits the exercise of, or is 523 | conditioned on the non-exercise of one or more of the rights that are 524 | specifically granted under this License. You may not convey a covered 525 | work if you are a party to an arrangement with a third party that is 526 | in the business of distributing software, under which you make payment 527 | to the third party based on the extent of your activity of conveying 528 | the work, and under which the third party grants, to any of the 529 | parties who would receive the covered work from you, a discriminatory 530 | patent license (a) in connection with copies of the covered work 531 | conveyed by you (or copies made from those copies), or (b) primarily 532 | for and in connection with specific products or compilations that 533 | contain the covered work, unless you entered into that arrangement, 534 | or that patent license was granted, prior to 28 March 2007. 535 | 536 | Nothing in this License shall be construed as excluding or limiting 537 | any implied license or other defenses to infringement that may 538 | otherwise be available to you under applicable patent law. 539 | 540 | 12. No Surrender of Others' Freedom. 541 | 542 | If conditions are imposed on you (whether by court order, agreement or 543 | otherwise) that contradict the conditions of this License, they do not 544 | excuse you from the conditions of this License. If you cannot convey a 545 | covered work so as to satisfy simultaneously your obligations under this 546 | License and any other pertinent obligations, then as a consequence you may 547 | not convey it at all. For example, if you agree to terms that obligate you 548 | to collect a royalty for further conveying from those to whom you convey 549 | the Program, the only way you could satisfy both those terms and this 550 | License would be to refrain entirely from conveying the Program. 551 | 552 | 13. Use with the GNU Affero General Public License. 553 | 554 | Notwithstanding any other provision of this License, you have 555 | permission to link or combine any covered work with a work licensed 556 | under version 3 of the GNU Affero General Public License into a single 557 | combined work, and to convey the resulting work. The terms of this 558 | License will continue to apply to the part which is the covered work, 559 | but the special requirements of the GNU Affero General Public License, 560 | section 13, concerning interaction through a network will apply to the 561 | combination as such. 562 | 563 | 14. Revised Versions of this License. 564 | 565 | The Free Software Foundation may publish revised and/or new versions of 566 | the GNU General Public License from time to time. Such new versions will 567 | be similar in spirit to the present version, but may differ in detail to 568 | address new problems or concerns. 569 | 570 | Each version is given a distinguishing version number. If the 571 | Program specifies that a certain numbered version of the GNU General 572 | Public License "or any later version" applies to it, you have the 573 | option of following the terms and conditions either of that numbered 574 | version or of any later version published by the Free Software 575 | Foundation. If the Program does not specify a version number of the 576 | GNU General Public License, you may choose any version ever published 577 | by the Free Software Foundation. 578 | 579 | If the Program specifies that a proxy can decide which future 580 | versions of the GNU General Public License can be used, that proxy's 581 | public statement of acceptance of a version permanently authorizes you 582 | to choose that version for the Program. 583 | 584 | Later license versions may give you additional or different 585 | permissions. However, no additional obligations are imposed on any 586 | author or copyright holder as a result of your choosing to follow a 587 | later version. 588 | 589 | 15. Disclaimer of Warranty. 590 | 591 | THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY 592 | APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT 593 | HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY 594 | OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, 595 | THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 596 | PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM 597 | IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF 598 | ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 599 | 600 | 16. Limitation of Liability. 601 | 602 | IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING 603 | WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS 604 | THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY 605 | GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE 606 | USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF 607 | DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD 608 | PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), 609 | EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF 610 | SUCH DAMAGES. 611 | 612 | 17. Interpretation of Sections 15 and 16. 613 | 614 | If the disclaimer of warranty and limitation of liability provided 615 | above cannot be given local legal effect according to their terms, 616 | reviewing courts shall apply local law that most closely approximates 617 | an absolute waiver of all civil liability in connection with the 618 | Program, unless a warranty or assumption of liability accompanies a 619 | copy of the Program in return for a fee. 620 | 621 | END OF TERMS AND CONDITIONS 622 | 623 | How to Apply These Terms to Your New Programs 624 | 625 | If you develop a new program, and you want it to be of the greatest 626 | possible use to the public, the best way to achieve this is to make it 627 | free software which everyone can redistribute and change under these terms. 628 | 629 | To do so, attach the following notices to the program. It is safest 630 | to attach them to the start of each source file to most effectively 631 | state the exclusion of warranty; and each file should have at least 632 | the "copyright" line and a pointer to where the full notice is found. 633 | 634 | 635 | Copyright (C) 636 | 637 | This program is free software: you can redistribute it and/or modify 638 | it under the terms of the GNU General Public License as published by 639 | the Free Software Foundation, either version 3 of the License, or 640 | (at your option) any later version. 641 | 642 | This program is distributed in the hope that it will be useful, 643 | but WITHOUT ANY WARRANTY; without even the implied warranty of 644 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 645 | GNU General Public License for more details. 646 | 647 | You should have received a copy of the GNU General Public License 648 | along with this program. If not, see . 649 | 650 | Also add information on how to contact you by electronic and paper mail. 651 | 652 | If the program does terminal interaction, make it output a short 653 | notice like this when it starts in an interactive mode: 654 | 655 | Copyright (C) 656 | This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. 657 | This is free software, and you are welcome to redistribute it 658 | under certain conditions; type `show c' for details. 659 | 660 | The hypothetical commands `show w' and `show c' should show the appropriate 661 | parts of the General Public License. Of course, your program's commands 662 | might be different; for a GUI interface, you would use an "about box". 663 | 664 | You should also get your employer (if you work as a programmer) or school, 665 | if any, to sign a "copyright disclaimer" for the program, if necessary. 666 | For more information on this, and how to apply and follow the GNU GPL, see 667 | . 668 | 669 | The GNU General Public License does not permit incorporating your program 670 | into proprietary programs. If your program is a subroutine library, you 671 | may consider it more useful to permit linking proprietary applications with 672 | the library. If this is what you want to do, use the GNU Lesser General 673 | Public License instead of this License. But first, please read 674 | . 675 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # binorg_decoding 2 | Bin-Organized Decoding (J. Neuro: Bae&Luck, 2018). 3 | 4 | Here you will find an updated version of Bae & Luck (2018) analysis scripts. We have updated the scripts to allow the use of bin-organized decoding, therefore allowing users to use EEGlab/ERPlab* to format and decode their own data more seamlessly. 5 | 6 | **ERPLAB Version V8.01 REQUIRED** 7 | Download ERPLAB here: https://github.com/lucklab/erplab/releases/download/8.01/erplab8.01.zip 8 | 9 | **Decoding Scripts** 10 | The MATLAB scripts used for the online webinar are found in this .zip file: https://github.com/ammsimmons/binorg_decoding/archive/master.zip 11 | 12 | Order of Operations: 13 | 1. Decoding_binOrg.m 14 | 2. SVM_ECOC_ERP_Decoding_2020.m \ 15 | --See Below for Link to Data on Box: 16 | https://ucdavis.box.com/s/aecglu8zqox52nc94f5jywj9mxktbf57 17 | 3. SVM_ECOC_Stat_Plot_Permutation_2020.m \ 18 | -- Required: "boundedline.m" & "inpaint_nans.m" (Kearney, 2020) \ 19 | -- See here: https://www.mathworks.com/matlabcentral/fileexchange/27485-boundedline-m 20 | 4. SVM_ECOC_ERP_PermutationTesting_2020.m 21 | 22 | Again, data used on our Decoding Webinar (2020) can be found at: 23 | https://ucdavis.box.com/s/aecglu8zqox52nc94f5jywj9mxktbf57 24 | 25 | Background: 26 | The scripts we use are an updated version of those found in Bae & Luck (2018). If you wish to see those on OSF, see them on this link https://osf.io/bpexa/ \ 27 | Our scripts above are much easier to adapt to use for your own data than the original 2018 scripts. -------------------------------------------------------------------------------- /SVM_ECOC_ERP_Decoding_2020.m: -------------------------------------------------------------------------------- 1 | % This is an updated function of Bae & Luck (2018) Decoding 2 | % pipeline that utilizes a nested bin-epoched data structure. 3 | % Refer to OSF: https://osf.io/29wre/ 4 | 5 | % NOTE: low-pass filtering to 6hz was applied to continuous EEG (prior to 6 | % binning). % Code to be used on a bin-epoched dataset for one 7 | % class only (e.g. Orientation) 8 | 9 | % NOTE: This script requires the 'fitcecoc' Matlab function. This function is a 10 | % part of the Matlab Statistics and Machine Learning toolbox. 11 | 12 | %Edited by Aaron Simmons (UC Davis) 13 | %Original Author: Gi-Yeul Bae (Arizona State University) 14 | 15 | function SVM_ECOC_ERP_Decoding(subs) 16 | 17 | % Parallelization: This script utilizes Matlab parallelization ... 18 | % if parallelization is not possible, change "parfor" to "for-loop" 19 | delete(gcp) 20 | parpool 21 | 22 | %% Check presence of Matlab Statistics and Machine Learning Toolbox 23 | % This toolbox is required for the SVM classification 24 | V = ver; 25 | Vname = {V.Name}; 26 | if ~(any(strcmp(Vname, 'Statistics and Machine Learning Toolbox'))) 27 | error('Error. Statistics and Machine Learning toolbox not found.'); 28 | end 29 | 30 | 31 | %% Subject List: 32 | if nargin ==0 33 | 34 | subs = [505, 506, 507, 508, 509, 510, 512, 514, 516, 517, 519, 520, 521, 523, 524, 525]; 35 | 36 | end 37 | 38 | nSubs = length(subs); %number of subjects 39 | 40 | %% Subject Filename(s) 41 | 42 | % Load Subject Datafiles 43 | dataLocation = pwd; % set directory of data set (default: pwd) 44 | fName = ['/Decoding_BE_']; % subject filename (subject # appended at end) 45 | 46 | % Save Decoding results 47 | saveLocation = pwd; % set save directory of data set (default: pwd) 48 | savName = ['/Orientation_Results_ERPbased_']; 49 | 50 | 51 | %% Parameters to set 52 | % Main structure is svmECOC. The output file is composed of svmECOC struct 53 | 54 | svmECOC.nBins = 16; % # of stimulus bins 55 | 56 | svmECOC.nIter = 10; % # of iterations 57 | 58 | svmECOC.nBlocks = 3; % # of blocks for cross-validation 59 | 60 | svmECOC.dataTime.pre = -500; % Set epoch start (from imported data) 61 | svmECOC.dataTime.post = 1496; % Set epoch end (from imported data) 62 | 63 | svmECOC.time = -500:20:1496; % time points of interest in the analysis 64 | % the time-points of interest is a resampling of preprocessed data 65 | % the time steps (20ms) here change the data to 1 data point per 20ms 66 | 67 | % Timepoints/sampling rate of preprocessed/loaded data 68 | svmECOC.window = 4; % 1 data point per 4 ms in the preprocessed data 69 | svmECOC.Fs = 250; % samplring rate of in the preprocessed data for filtering 70 | 71 | % Equalalize trials across bins? 72 | equalT = 1; % equal trials acr bin = 1; use as many trials within bin = 0 73 | 74 | % The electrode channel list is mapped 1:1 with respect to your 75 | % electrode channel configuration (e.g., channel 1 is FP1 in our EEG cap) 76 | % Check if the label for each electrode corresponds to the electrode 77 | % labeling of your own EEG system 78 | ReleventChan = sort([2,3,4,18,19, 5,6,20, 7,8,21,9,10,11,12,13,14, 22,23,24,25,26,27, 15,16,1,17]); %electrodes 79 | % here, we removed external EOG electrodes & mastoid ref, for 27 electrodes 80 | 81 | svmECOC.nElectrodes = length(ReleventChan); % # of electrode included in the analysis 82 | 83 | 84 | % for brevity in analysis 85 | nBins = svmECOC.nBins; 86 | 87 | ogWin = svmECOC.window; 88 | 89 | nIter = svmECOC.nIter; 90 | 91 | nBlocks = svmECOC.nBlocks; 92 | 93 | dataTime = svmECOC.dataTime; 94 | 95 | times = svmECOC.time; 96 | 97 | nElectrodes = svmECOC.nElectrodes; 98 | 99 | nSamps = length(svmECOC.time); 100 | 101 | 102 | %% Step 9: Loop through participants 103 | for s = 1:nSubs %decoding is performed within each subject independently 104 | sn = subs(s); 105 | 106 | %progress output to command window 107 | fprintf('Subject:\t%d\n',sn) 108 | 109 | %% Parameters to set per subject 110 | currentSub = num2str(sn); 111 | loadThis = strcat(dataLocation,fName,currentSub,'.mat'); 112 | 113 | % where to save decoding output file name. 114 | % File (.mat) will contain decoding results. 115 | OutputfName = strcat(saveLocation,savName,currentSub,'.mat'); 116 | 117 | 118 | %% Data Loading/preallocation per subject 119 | % loads bin_organized data 120 | load(loadThis) 121 | 122 | % grab EEG data from bin-list organized data 123 | eegs = binorgEEG.binwise_data; 124 | nPerBin = binorgEEG.n_trials_this_bin; 125 | 126 | % set up time points 127 | % we create index of timpoint of interests from original data 128 | tois = ismember(dataTime.pre:ogWin:dataTime.post,svmECOC.time); 129 | 130 | % # of trials 131 | svmECOC.nTrials = (sum(nPerBin)); 132 | 133 | % Preallocate Matrices 134 | svm_predict = nan(nIter,nSamps,nBlocks,nBins); % a matrix to save prediction from SVM 135 | tst_target = nan(nIter,nSamps,nBlocks,nBins); % a matrix to save true target values 136 | 137 | % create svmECOC.block structure to save block assignments 138 | svmECOC.blocks=struct(); 139 | 140 | if nBins ~= max(size(eegs)) 141 | error('Error. nBins dne # of bins in dataset!'); 142 | % Code to be used on a bin-epoched dataset for one 143 | % class only (e.g. Orientation). Users may 144 | % try to bin multiple classes in one BDF. 145 | % This is not currently allowed. 146 | end 147 | 148 | 149 | %% Step 8: Loop through each iteration with random shuffling 150 | tic % start timing iteration loop 151 | 152 | for iter = 1:nIter 153 | 154 | %% Obtaining AVG. EEG (ERP spatial dist) 155 | % within each block at each time point 156 | % stored in blockDat_filtData 157 | 158 | %preallocate & rewrite per each iteration 159 | blockDat_filtData = nan(nBins*nBlocks,nElectrodes,nSamps); 160 | labels = nan(nBins*nBlocks,1); % bin labels for averaged & filtered EEG data 161 | blockNum = nan(nBins*nBlocks,1); % block numbers for averaged & filtered EEG data 162 | bCnt = 1; %initialize binblock counter 163 | 164 | % this code operates and creates ERPs at each bin 165 | % and organizes that into approprate block 166 | 167 | %% shuffling, binning, & averaging 168 | for bin = 1:nBins 169 | 170 | if equalT == 1 %equal trials across bins 171 | % find bin with fewest trial 172 | minCnt = min(nPerBin); 173 | 174 | % max # of trials such that # of trials for each bin ... 175 | % can be equated within each block 176 | nPerBinBlock = floor(minCnt/nBlocks); 177 | 178 | %Obtain index within each shuffled bin 179 | shuffBin = randperm((nPerBinBlock*nBlocks))'; 180 | 181 | %Preallocate arrays 182 | blocks = nan(size(shuffBin)); 183 | shuffBlocks = nan(size(shuffBin)); 184 | 185 | %arrage block order within bins 186 | x = repmat((1:nBlocks)',nPerBinBlock,1); 187 | shuffBlocks(shuffBin) = x; 188 | 189 | 190 | else 191 | % We will use as many possible trials per 192 | % bin having accounted already for artifacts 193 | 194 | %Drop excess trials 195 | nPerBinBlock = floor(nPerBin/nBlocks); %array for nPerBin 196 | 197 | %Obtain index within each shuffled bin 198 | shuffBin = randperm((nPerBinBlock(bin))*nBlocks)'; 199 | 200 | %Preallocate arrays 201 | 202 | blocks = nan(size(shuffBin)); 203 | shuffBlocks = nan(size(shuffBin)); 204 | 205 | %arrage block order within bins 206 | x = repmat((1:nBlocks)',nPerBinBlock(bin),1); 207 | shuffBlocks(shuffBin) = x; 208 | end 209 | 210 | %unshuffled block assignment 211 | blocks(shuffBin) = shuffBlocks; 212 | 213 | % save block assignment 214 | blockID = ['iter' num2str(iter) 'bin' num2str(bin)]; 215 | 216 | svmECOC.blocks.(blockID) = blocks; % block assignment 217 | 218 | 219 | %create ERP average and place into blockDat_filtData struct 220 | %grab current bin with correct # of electrodes & samps 221 | eeg_now = eegs(bin).data(ReleventChan,:,:); 222 | 223 | %here, we create blockDat_filtData. 224 | %this results in nBins*nBlocks amount of ERP spatial 225 | %distributions (across nChans) at each sample/timepoint 226 | 227 | %% Step 1: computing ERPs based on random subset of trials for each block 228 | for bl = 1:nBlocks 229 | 230 | blockDat_filtData(bCnt,:,:) = squeeze(mean(eeg_now(:,tois,blocks==bl),3)); 231 | 232 | labels(bCnt) = bin; %used for arranging classification obj. 233 | 234 | blockNum(bCnt) = bl; 235 | 236 | bCnt = bCnt+1; 237 | 238 | end 239 | 240 | end 241 | 242 | 243 | %% Step 7: Loop through each timepoint 244 | % Do SVM_ECOC at each time point 245 | parfor t = 1:nSamps 246 | 247 | % grab data for timepoint t 248 | toi = ismember(times,times(t)-svmECOC.window/2:times(t)+svmECOC.window/2); 249 | 250 | % average across time window of interest 251 | % here, you can parse nBin*nBlock across all channels (ERP spatial dist) 252 | dataAtTimeT = squeeze(mean(blockDat_filtData(:,:,toi),3)); 253 | 254 | %% Step 6: Cross-validation for-loop 255 | for i=1:nBlocks % loop through blocks, holding each out as the test set 256 | 257 | 258 | %% Step 2 & Step 4: Assigning training and testing data sets 259 | trnl = labels(blockNum~=i); % training labels 260 | 261 | tstl = labels(blockNum==i); % test labels 262 | 263 | trnD = dataAtTimeT(blockNum~=i,:); % training data 264 | 265 | tstD = dataAtTimeT(blockNum==i,:); % test data 266 | 267 | %% Step 3: Training 268 | mdl = fitcecoc(trnD,trnl, 'Coding','onevsall','Learners','SVM' ); %train support vector mahcine 269 | %% Step 5: Testing 270 | LabelPredicted = predict(mdl, tstD); % predict classes for new data 271 | 272 | svm_predict(iter,t,i,:) = LabelPredicted; % save predicted labels 273 | 274 | tst_target(iter,t,i,:) = tstl; % save true target labels 275 | 276 | 277 | end % end of block: Step 6: cross-validation 278 | 279 | end % end of time points: Step 7: Decoding each time point 280 | 281 | end % end of iteration: Step 8: iteration with random shuffling 282 | 283 | toc % stop timing the iteration loop 284 | 285 | %output decoding results in main svmECOC structure 286 | svmECOC.targets = tst_target; 287 | svmECOC.modelPredict = svm_predict; 288 | svmECOC.nBlocks = nBlocks; 289 | 290 | save(OutputfName,'svmECOC','-v7.3'); 291 | 292 | end % end of subject loop: Step 9: Decoding for each participant -------------------------------------------------------------------------------- /SVM_ECOC_ERP_PermutationTesting_2020.m: -------------------------------------------------------------------------------- 1 | % This is an updated script that computes average decoding accuracy 2 | % and performes cluster-based permutation analysis. 3 | 4 | % In order to perform cluster-based permutation analysis, users must use 5 | % this script to simulate null-distribution to test against. 6 | 7 | % This script needs all the original decoding results in the same 8 | % directory. These simulations utilize these subject decoding results. 9 | 10 | %Edited by Aaron Simmons (UC Davis) 11 | %Original Author: Gi-Yeul Bae (ASU) 12 | 13 | %% Parameters 14 | % Must match SVM_ECOC_Stat_Plot_Permutation_2020.m 15 | 16 | % Subject List 17 | subList = [505 506 507 508 509 510 512 514 516 517 519 520 521 523 524 525]; 18 | Nsub = length(subList); 19 | 20 | % Decoding Parameters (must match svmECOC object) 21 | Nblock = 3; % cross-validation 22 | Nitr = 10; % iteration 23 | Ntp = 100; % # of time points 24 | NBins = 16; % # of stimulus bins 25 | 26 | % Permutation Simulation Parameters 27 | NPermutations = 10000; %# of permutations 28 | permutedT = nan(1,NPermutations); %preallocate memory 29 | NTrials = zeros(Nsub,1); %Preallocate trials from svmECOC object 30 | chancelvl = 1/NBins; %chance level of avg. decoding 31 | 32 | % resampled timepoints from decoding (20ms/tp) 33 | tm = -500:20:1496; 34 | 35 | %create empty matrix 36 | AverageAccuracy = zeros(Nsub,Ntp); 37 | 38 | % which time points to permute? relevantTime to conduct stats 39 | tStart = find(tm==220); % start of retention interval 40 | ntPerm = find(tm==1480); % end of retention interval (in this case, end of epoch) 41 | 42 | % Load Individual subject filenames 43 | fileLocation = pwd; 44 | fName = ['/Orientation_Results_ERPbased_']; 45 | 46 | % Save Filename (saves .mat file to current directory) 47 | saveName = ['Permuted_T_mass.mat']; 48 | 49 | tic 50 | 51 | for permt = 1: NPermutations 52 | 53 | for sub = 1:Nsub 54 | DecodingAccuracy = zeros(Ntp,Nblock,Nitr); 55 | 56 | % Load Data 57 | readThis =strcat(fileLocation,fName,num2str(subList(sub)),'.mat'); 58 | load(readThis) 59 | 60 | NTrials(sub,1) = svmECOC.nTrials; 61 | 62 | svmPrediction = squeeze(svmECOC.modelPredict); 63 | tstTargets = squeeze(svmECOC.targets); 64 | 65 | for block = 1:Nblock 66 | for itr = 1:Nitr 67 | %% assign random target ID for Permutation Testing 68 | Answer = shuffle(1:NBins)'; 69 | 70 | for tp = 1:Ntp % for stimulus duration of 0 ms ~ 1480 ms 71 | 72 | prediction = squeeze(svmPrediction(itr,tp,block,:)); % this is predictions from models 73 | Err = Answer - prediction; 74 | ACC = mean(Err==0); 75 | DecodingAccuracy(tp,block,itr) = ACC; % average decoding accuracy 76 | 77 | end 78 | 79 | end 80 | end 81 | 82 | grandAvg = squeeze(mean(mean(DecodingAccuracy,2),3)); 83 | 84 | smoothed = nan(1,Ntp); 85 | for tAvg = 1:Ntp 86 | if tAvg ==1 87 | smoothed(tAvg) = mean(grandAvg((tAvg):(tAvg+2))); 88 | elseif tAvg ==2 89 | smoothed(tAvg) = mean(grandAvg((tAvg-1):(tAvg+2))); 90 | elseif tAvg == (Ntp-1) 91 | smoothed(tAvg) = mean(grandAvg((tAvg-2):(tAvg+1))); 92 | elseif tAvg == Ntp 93 | smoothed(tAvg) = mean(grandAvg((tAvg-2):(tAvg))); 94 | else 95 | smoothed(tAvg) = mean(grandAvg((tAvg-2):(tAvg+2))); 96 | end 97 | 98 | end 99 | 100 | 101 | AverageAccuracy(sub,:) =smoothed; % average across iteration and block 102 | 103 | clear svmECOC 104 | 105 | end %End of subject 106 | 107 | % AverageAccuracy = Nsub x Ncond (Attend vs Ignore) x Ntyp (Evoked VS Total) x Ntp; 108 | 109 | %now compute average accuracy across participants 110 | subAverage = squeeze(mean(AverageAccuracy,1)); 111 | seAverage = squeeze(std(AverageAccuracy,1))/sqrt(Nsub); 112 | 113 | %% do cluster mass analyses 114 | 115 | releventTime = tStart:ntPerm; % during the retention interval 116 | 117 | Ps = nan(2,length(releventTime)); 118 | for i = 1:length(releventTime) % make sure this time range is correct 119 | tp = releventTime(i); 120 | 121 | [H,P,CI,STATS] = ttest(AverageAccuracy(:,tp), chancelvl,'tail','right'); % Test Against Zero 122 | 123 | Ps(1,i) = STATS.tstat; 124 | Ps(2,i) = P; 125 | end 126 | % find significant points 127 | candid = Ps(2,:) <= .05; 128 | 129 | candid_marked = zeros(1,length(candid)); 130 | candid_marked(1,1) = candid(1,1); 131 | candid_marked(1,length(candid)) = candid(1,length(candid)); 132 | %remove orphan time points 133 | for i = 2:length(releventTime)-1 134 | 135 | if candid(1,i-1) == 0 && candid(1,i) ==1 && candid(1,i+1) ==0 136 | candid_marked(1,i) = 0; 137 | else 138 | candid_marked(1,i) = candid(1,i); 139 | end 140 | 141 | end 142 | 143 | % combine whole time range with relevent time & significant information 144 | clusters = zeros(length(tm),1); 145 | clusterT = zeros(length(tm),1); 146 | clusters(releventTime,1) = candid_marked; % significant or not 147 | clusterT(releventTime,1) = Ps(1,:); % t values 148 | clusterTsum = sum(Ps(1,logical(candid_marked))); 149 | 150 | %%find how many clusters are there, and compute summed T of each cluster 151 | tmp = zeros(10,25); % creates a matrix with arbitrary size (n cluster x size of each cluster) 152 | cl = 0; 153 | member = 0; 154 | for i = 2:length(clusters)-1 155 | 156 | 157 | if clusters(i-1) ==0 && clusters(i) == 1 && clusters(i+1) == 1 158 | cl = cl+1; 159 | member = member +1; 160 | tmp(cl,member) = i; 161 | 162 | elseif clusters(i-1) ==1 && clusters(i) == 1 && clusters(i+1) == 0 163 | if i == 2 164 | cl = cl +1; 165 | member = member +1; 166 | tmp(cl,member) = i; 167 | member = 0; 168 | 169 | else 170 | member = member +1; 171 | tmp(cl,member) = i; 172 | member = 0; 173 | end 174 | 175 | 176 | elseif clusters(i-1) ==1 && clusters(i) == 1 && clusters(i+1) == 1 177 | if i ==2 178 | cl = cl+1; 179 | member = member +1; 180 | tmp(cl,member) = i; 181 | else 182 | member = member +1; 183 | tmp(cl,member) = i; 184 | end 185 | 186 | 187 | else 188 | 189 | 190 | end 191 | end 192 | 193 | 194 | HowManyClusters = cl; 195 | a = tmp(1:cl,:); 196 | eachCluster = a(:,logical(sum(a,1)~=0)); 197 | 198 | %now, compute summed T of each cluster 199 | dat_clusterSumT = zeros(HowManyClusters,1); 200 | for c = 1:HowManyClusters 201 | dat_clusterSumT(c,1) = sum(clusterT(eachCluster(c,eachCluster(c,:) ~=0))); 202 | end 203 | 204 | if size(dat_clusterSumT,1) > 0 % if there is at least one signifiant cluster 205 | permutedT(1,permt) = max(dat_clusterSumT); 206 | else 207 | permutedT(1,permt) = 0; 208 | end 209 | if rem(permt,(NPermutations/10)) == 0 210 | progress = 100*(permt/NPermutations); 211 | whatToPrint = strcat(num2str(progress),'% is done.'); 212 | disp(whatToPrint) 213 | toc % end of one permutation 214 | tic 215 | end 216 | 217 | end % end of simulation 218 | 219 | permutedT = sort(permutedT); 220 | save(saveName,'permutedT'); 221 | -------------------------------------------------------------------------------- /SVM_ECOC_Stat_Plot_Permutation_2020.m: -------------------------------------------------------------------------------- 1 | % This is an updated script that computes average decoding accuracy 2 | % and performes cluster-based permutation analysis. 3 | 4 | % In order to perform cluster-based permutation analysis, users must have 5 | % already simulated null-distribution to test against. See 6 | % "SVM_ECOC_ERP_PermutationTesting_revised.". Plot function requires matlab 7 | % function "boundedline" & "inpaint_nans" (Kearny 2020) in working directory. 8 | % see here: https://www.mathworks.com/matlabcentral/fileexchange/27485-boundedline-m 9 | 10 | % Edited by Aaron Simmons (UC Davis) 11 | % Original Author: Gi-Yeul Bae (Arizona State University) 12 | 13 | % Note: Randomization routine included in this analysis (e.g., random integer generator) 14 | % can produce stats slightly different from those reported in the paper. 15 | % Gi-Yeul Bae 2017-10-3 16 | 17 | %% Parameters 18 | 19 | % Subject List 20 | subList = [505 506 507 508 509 510 512 514 516 517 519 520 521 523 524 525]; 21 | Nsub = length(subList); % N subjects 22 | 23 | % Decoding Parameters (must match svmECOC object) 24 | Nblock = 3; % cross-validation 25 | Nitr = 10; % iteration 26 | Ntp = 100; % # of time points 27 | NBins = 16; % # of stimulus bins 28 | 29 | % Stats (see "Plot Significant Clusters" (line 204) for resulting plot formatting) 30 | ClusterMassAnalysis = 0; % 0 = do not perform / 1 = perform 31 | NPermutations = 10000; % N permutations of null distribution 32 | chancelvl = 1/NBins; %chance lvl of avg. decoding 33 | tm = -500:20:1496; % resampled timepoints from decoding (20ms/tp) 34 | %define time point for statistical analysis 35 | releventTime = 37:100; % corresponds to [220, 1480] ms 36 | % find(tm==220); % obtain index of relevant time points in sampling units 37 | 38 | % Individual subject filenames 39 | fileLocation = pwd; 40 | fName = ['Processed/Orientation_Results_ERPbased_']; 41 | 42 | 43 | %% Plotting Decoding Results (no stats) 44 | 45 | % Create/preallocate empty matrix 46 | AverageAccuracy = nan(Nsub,Ntp); 47 | 48 | % First we will obtain average decoding accuracy within subjects at each 49 | % timepoint. This results in size(AverageAccuracy). 50 | 51 | for sub = 1:Nsub 52 | DecodingAccuracy = nan(Ntp,Nblock,Nitr); 53 | % We will compute decoding accuracy per subject in DecodingAccuracy, 54 | % enter DecodingAccuracy into AverageAccuray, then overwrite next subj. 55 | 56 | %% load SVM_ECOC output files 57 | readThis =strcat(fileLocation,filesep,fName,num2str(subList(sub)),'.mat'); 58 | load(readThis) 59 | 60 | % Obtain predictions from SVM-ECOC model 61 | svmPrediction = squeeze(svmECOC.modelPredict); 62 | tstTargets = squeeze(svmECOC.targets); 63 | %clear svmECOC (is now a large unnecessary objet) 64 | clear svmECOC 65 | 66 | %% Step 5: Compute decoding accuracy of each decoding trial 67 | for block = 1:Nblock 68 | for itr = 1:Nitr 69 | for tp = 1:Ntp 70 | 71 | prediction = squeeze(svmPrediction(itr,tp,block,:)); % this is predictions from models 72 | TrueAnswer = squeeze(tstTargets(itr,tp,block,:)); % this is predictions from models 73 | Err = TrueAnswer - prediction; %compute error. No error = 0 74 | ACC = mean(Err==0); %Correct hit = 0 (avg propotion of vector of 1s and 0s) 75 | DecodingAccuracy(tp,block,itr) = ACC; % average decoding accuracy at tp & block 76 | 77 | end 78 | end 79 | end 80 | 81 | % Average across block and iterations 82 | grandAvg = squeeze(mean(mean(DecodingAccuracy,2),3)); 83 | 84 | % Perform temporal smoothing (5 point moving avg) 85 | smoothed = nan(1,Ntp); 86 | for tAvg = 1:Ntp 87 | if tAvg ==1 88 | smoothed(tAvg) = mean(grandAvg((tAvg):(tAvg+2))); 89 | elseif tAvg ==2 90 | smoothed(tAvg) = mean(grandAvg((tAvg-1):(tAvg+2))); 91 | elseif tAvg == (Ntp-1) 92 | smoothed(tAvg) = mean(grandAvg((tAvg-2):(tAvg+1))); 93 | elseif tAvg == Ntp 94 | smoothed(tAvg) = mean(grandAvg((tAvg-2):(tAvg))); 95 | else 96 | smoothed(tAvg) = mean(grandAvg((tAvg-2):(tAvg+2))); 97 | end 98 | 99 | end 100 | 101 | % Save smoothe data 102 | AverageAccuracy(sub,:) =smoothed; % average across iteration and block 103 | 104 | end %End of subject 105 | 106 | %compute average accuracy across participants and SE of the mean. 107 | subAverage = squeeze(mean(AverageAccuracy,1)); 108 | seAverage = squeeze(std(AverageAccuracy,1))/sqrt(Nsub); 109 | 110 | %% Visualization:Plotting the decoding accuracy 111 | % across all subjects at each timepoint 112 | % Not publication quality 113 | figure(1) 114 | cl=colormap(parula(50)); 115 | plot(tm, subAverage); %plot 116 | boundedline(tm,subAverage,seAverage,'cmap',cl(42,:),'alpha','transparency',0.35) 117 | line([tm(1),tm(length(tm))],[chancelvl,chancelvl]); %chance line 118 | 119 | 120 | %% Perform cluster mass analyses 121 | if ClusterMassAnalysis == 1 122 | 123 | % t-test at each relevent time point 124 | Ps = nan(2,length(releventTime)); 125 | for i = 1:length(releventTime) 126 | tp = releventTime(i); 127 | 128 | [H,P,CI,STATS] = ttest(AverageAccuracy(:,tp), chancelvl, 'tail', 'right'); % one sample t-test 129 | 130 | Ps(1,i) = STATS.tstat; 131 | Ps(2,i) = P; 132 | end 133 | 134 | % find significant time points 135 | candid = Ps(2,:) <= .05; 136 | 137 | %remove orphan time points 138 | candid_woOrphan = candid; 139 | candid_woOrphan(1,1) = candid(1,1); 140 | for i = 2:(length(releventTime)-1) 141 | 142 | if candid(1,i-1) == 0 && candid(1,i) ==1 && candid(1,i+1) ==0 143 | candid_woOrphan(1,i) = 0; 144 | else 145 | candid_woOrphan(1,i) = candid(1,i); 146 | end 147 | 148 | end 149 | 150 | % combine whole time range with relevent time & significant information 151 | clusters = zeros(length(tm),1); 152 | clusterT = zeros(length(tm),1); 153 | clusters(releventTime,1) = candid_woOrphan; 154 | clusterT(releventTime,1) = Ps(1,:); 155 | clusterTsum = sum(Ps(1,logical(candid_woOrphan))); 156 | 157 | %%find how many clusters are there, and compute summed T of each cluster 158 | tmp = zeros(10,300); 159 | cl = 0; 160 | member = 0; 161 | for i = 2:length(clusters)-1 162 | 163 | if clusters(i-1) ==0 && clusters(i) == 1 && clusters(i+1) == 1 164 | cl = cl+1; 165 | member = member +1; 166 | tmp(cl,member) = i; 167 | 168 | elseif clusters(i-1) ==1 && clusters(i) == 1 && clusters(i+1) == 0 169 | member = member +1; 170 | tmp(cl,member) = i; 171 | member = 0; 172 | elseif clusters(i-1) ==1 && clusters(i) == 1 && clusters(i+1) == 1 173 | member = member +1; 174 | tmp(cl,member) = i; 175 | 176 | else 177 | 178 | end 179 | end 180 | 181 | 182 | HowManyClusters = cl; 183 | a = tmp(1:cl,:); % subset significant clusters 184 | eachCluster = a(:,logical(sum(a,1)~=0)); % cut the size at the maximum cluster 185 | 186 | %now, compute summed T of each cluster 187 | dat_clusterSumT = nan(HowManyClusters,1); 188 | for c = 1:HowManyClusters 189 | dat_clusterSumT(c,1) = sum(clusterT(eachCluster(c,eachCluster(c,:) ~=0))); 190 | end 191 | 192 | 193 | 194 | %% note: Load Simulation tests (simulation takes very long time) 195 | load('Permuted_T_mass.mat') 196 | 197 | 198 | %% find critical t-value 199 | cutOff = NPermutations - NPermutations * 0.05; %one tailed 200 | critT = permutedT(cutOff); % t-mass of top 95% 201 | sigCluster = dat_clusterSumT > critT; 202 | 203 | 204 | %% Plot significant clusters 205 | figure(2) 206 | cl=colormap(parula(50)); 207 | 208 | % draw average accuracy 209 | accEst = squeeze(subAverage); 210 | % draw clusters 211 | draw = eachCluster(sigCluster,:); 212 | draw = sort(reshape(draw,1,size(draw,1)*size(draw,2))); 213 | draw = draw(draw>0); 214 | 215 | w = zeros(Ntp,1); 216 | w(draw)=1; 217 | a = area(1:length(tm), accEst.*w'); 218 | a.EdgeColor = 'none'; 219 | a.FaceColor = [0.8,0.8,0.8]; 220 | child = get(a,'Children'); 221 | set(child,'FaceAlpha',0.9) 222 | % draw mean and SE of average decoding accuracy 223 | hold on 224 | mEI = boundedline(1:length(tm),subAverage,seAverage, 'cmap',cl(42,:),'alpha','transparency',0.35); 225 | 226 | %% Plot Formatting 227 | xlabel('Time (ms)');ylabel('Decoding Accuracy') 228 | ax = gca; 229 | ax.YLim = [0.05, 0.15]; 230 | ax.YTick = [0,0.02,0.04,0.06,0.08,0.10,0.12,0.14]; 231 | ax.XTick = [1 26 51 76 100]; 232 | ax.XTickLabel = {'-500','0','500','1000','1500'}; 233 | h = line(1:length(tm),chancelvl* ones(1,Ntp)); 234 | h.LineStyle = '--'; 235 | h.Color = [0.1,0.1,0.1]; 236 | hold off 237 | 238 | saveas(figure(2),'plot_decoding_accuracy_with_stat','pdf') 239 | end -------------------------------------------------------------------------------- /boundedline.m: -------------------------------------------------------------------------------- 1 | function varargout = boundedline(varargin) 2 | %BOUNDEDLINE Plot a line with shaded error/confidence bounds 3 | % 4 | % [hl, hp] = boundedline(x, y, b) 5 | % [hl, hp] = boundedline(x, y, b, linespec) 6 | % [hl, hp] = boundedline(x1, y1, b1, linespec1, x2, y2, b2, linespec2) 7 | % [hl, hp] = boundedline(..., 'alpha') 8 | % [hl, hp] = boundedline(..., ax) 9 | % [hl, hp] = boundedline(..., 'transparency', trans) 10 | % [hl, hp] = boundedline(..., 'orientation', orient) 11 | % [hl, hp] = boundedline(..., 'nan', nanflag) 12 | % [hl, hp] = boundedline(..., 'cmap', cmap) 13 | % 14 | % Input variables: 15 | % 16 | % x, y: x and y values, either vectors of the same length, matrices 17 | % of the same size, or vector/matrix pair where the row or 18 | % column size of the array matches the length of the vector 19 | % (same requirements as for plot function). 20 | % 21 | % b: npoint x nside x nline array. Distance from line to 22 | % boundary, for each point along the line (dimension 1), for 23 | % each side of the line (lower/upper or left/right, depending 24 | % on orientation) (dimension 2), and for each plotted line 25 | % described by the preceding x-y values (dimension 3). If 26 | % size(b,1) == 1, the bounds will be the same for all points 27 | % along the line. If size(b,2) == 1, the bounds will be 28 | % symmetrical on both sides of the lines. If size(b,3) == 1, 29 | % the same bounds will be applied to all lines described by 30 | % the preceding x-y arrays (only applicable when either x or 31 | % y is an array). Bounds cannot include Inf, -Inf, or NaN, 32 | % 33 | % linespec: line specification that determines line type, marker 34 | % symbol, and color of the plotted lines for the preceding 35 | % x-y values. 36 | % 37 | % 'alpha': if included, the bounded area will be rendered with a 38 | % partially-transparent patch the same color as the 39 | % corresponding line(s). If not included, the bounded area 40 | % will be an opaque patch with a lighter shade of the 41 | % corresponding line color. 42 | % 43 | % ax: handle of axis where lines will be plotted. If not 44 | % included, the current axis will be used. 45 | % 46 | % transp: Scalar between 0 and 1 indicating with the transparency or 47 | % intensity of color of the bounded area patch. Default is 48 | % 0.2. 49 | % 50 | % orient: direction to add bounds 51 | % 'vert': add bounds in vertical (y) direction (default) 52 | % 'horiz': add bounds in horizontal (x) direction 53 | % 54 | % nanflag: Sets how NaNs in the boundedline patch should be handled 55 | % 'fill': fill the value based on neighboring values, 56 | % smoothing over the gap 57 | % 'gap': leave a blank space over/below the line 58 | % 'remove': drop NaNs from patches, creating a linear 59 | % interpolation over the gap. Note that this 60 | % applies only to the bounds; NaNs in the line will 61 | % remain. 62 | % 63 | % cmap: n x 3 colormap array. If included, lines will be colored 64 | % (in order of plotting) according to this colormap, 65 | % overriding any linespec or default colors. 66 | % 67 | % Output variables: 68 | % 69 | % hl: handles to line objects 70 | % 71 | % hp: handles to patch objects 72 | % 73 | % Example: 74 | % 75 | % x = linspace(0, 2*pi, 50); 76 | % y1 = sin(x); 77 | % y2 = cos(x); 78 | % e1 = rand(size(y1))*.5+.5; 79 | % e2 = [.25 .5]; 80 | % 81 | % ax(1) = subplot(2,2,1); 82 | % [l,p] = boundedline(x, y1, e1, '-b*', x, y2, e2, '--ro'); 83 | % outlinebounds(l,p); 84 | % title('Opaque bounds, with outline'); 85 | % 86 | % ax(2) = subplot(2,2,2); 87 | % boundedline(x, [y1;y2], rand(length(y1),2,2)*.5+.5, 'alpha'); 88 | % title('Transparent bounds'); 89 | % 90 | % ax(3) = subplot(2,2,3); 91 | % boundedline([y1;y2], x, e1(1), 'orientation', 'horiz') 92 | % title('Horizontal bounds'); 93 | % 94 | % ax(4) = subplot(2,2,4); 95 | % boundedline(x, repmat(y1, 4,1), permute(0.5:-0.1:0.2, [3 1 2]), ... 96 | % 'cmap', cool(4), 'transparency', 0.5); 97 | % title('Multiple bounds using colormap'); 98 | 99 | 100 | % Copyright 2010 Kelly Kearney 101 | 102 | %-------------------- 103 | % Parse input 104 | %-------------------- 105 | 106 | % Alpha flag 107 | 108 | isalpha = cellfun(@(x) ischar(x) && strcmp(x, 'alpha'), varargin); 109 | if any(isalpha) 110 | usealpha = true; 111 | varargin = varargin(~isalpha); 112 | else 113 | usealpha = false; 114 | end 115 | 116 | % Axis 117 | 118 | isax = cellfun(@(x) isscalar(x) && ishandle(x) && strcmp('axes', get(x,'type')), varargin); 119 | if any(isax) 120 | hax = varargin{isax}; 121 | varargin = varargin(~isax); 122 | else 123 | hax = gca; 124 | end 125 | 126 | % Transparency 127 | 128 | [found, trans, varargin] = parseparam(varargin, 'transparency'); 129 | 130 | if ~found 131 | trans = 0.2; 132 | end 133 | 134 | if ~isscalar(trans) || trans < 0 || trans > 1 135 | error('Transparency must be scalar between 0 and 1'); 136 | end 137 | 138 | % Orientation 139 | 140 | [found, orient, varargin] = parseparam(varargin, 'orientation'); 141 | 142 | if ~found 143 | orient = 'vert'; 144 | end 145 | 146 | if strcmp(orient, 'vert') 147 | isvert = true; 148 | elseif strcmp(orient, 'horiz') 149 | isvert = false; 150 | else 151 | error('Orientation must be ''vert'' or ''horiz'''); 152 | end 153 | 154 | 155 | % Colormap 156 | 157 | [hascmap, cmap, varargin] = parseparam(varargin, 'cmap'); 158 | 159 | 160 | % NaN flag 161 | 162 | [found, nanflag, varargin] = parseparam(varargin, 'nan'); 163 | if ~found 164 | nanflag = 'fill'; 165 | end 166 | if ~ismember(nanflag, {'fill', 'gap', 'remove'}) 167 | error('Nan flag must be ''fill'', ''gap'', or ''remove'''); 168 | end 169 | 170 | % X, Y, E triplets, and linespec 171 | 172 | [x,y,err,linespec] = deal(cell(0)); 173 | while ~isempty(varargin) 174 | if length(varargin) < 3 175 | error('Unexpected input: should be x, y, bounds triplets'); 176 | end 177 | if all(cellfun(@isnumeric, varargin(1:3))) 178 | x = [x varargin(1)]; 179 | y = [y varargin(2)]; 180 | err = [err varargin(3)]; 181 | varargin(1:3) = []; 182 | else 183 | if any(cellfun(@(x) isa(x, 'datetime'), varargin(1:3))) 184 | % Special error message for most likely culprit: datetimes 185 | error('boundedline cannot support datetime input due to incompatibility between patches and datetime axes; please convert to datenumbers instead'); 186 | else 187 | % Otherwise 188 | error('Unexpected input: should be numeric x, y, bounds triplets'); 189 | end 190 | end 191 | if ~isempty(varargin) && ischar(varargin{1}) 192 | linespec = [linespec varargin(1)]; 193 | varargin(1) = []; 194 | else 195 | linespec = [linespec {[]}]; 196 | end 197 | end 198 | 199 | %-------------------- 200 | % Reformat x and y 201 | % for line and patch 202 | % plotting 203 | %-------------------- 204 | 205 | % Calculate y values for bounding lines 206 | 207 | plotdata = cell(0,7); 208 | 209 | htemp = figure('visible', 'off'); 210 | for ix = 1:length(x) 211 | 212 | % Get full x, y, and linespec data for each line (easier to let plot 213 | % check for properly-sized x and y and expand values than to try to do 214 | % it myself) 215 | 216 | try 217 | if isempty(linespec{ix}) 218 | hltemp = plot(x{ix}, y{ix}); 219 | else 220 | hltemp = plot(x{ix}, y{ix}, linespec{ix}); 221 | end 222 | catch 223 | close(htemp); 224 | error('X and Y matrices and/or linespec not appropriate for line plot'); 225 | end 226 | 227 | linedata = get(hltemp, {'xdata', 'ydata', 'marker', 'linestyle', 'color'}); 228 | 229 | nline = size(linedata,1); 230 | 231 | % Expand bounds matrix if necessary 232 | 233 | if nline > 1 234 | if ndims(err{ix}) == 3 235 | err2 = squeeze(num2cell(err{ix},[1 2])); 236 | else 237 | err2 = repmat(err(ix),nline,1); 238 | end 239 | else 240 | err2 = err(ix); 241 | end 242 | 243 | % Figure out upper and lower bounds 244 | 245 | [lo, hi] = deal(cell(nline,1)); 246 | for iln = 1:nline 247 | 248 | x2 = linedata{iln,1}; 249 | y2 = linedata{iln,2}; 250 | nx = length(x2); 251 | 252 | if isvert 253 | lineval = y2; 254 | else 255 | lineval = x2; 256 | end 257 | 258 | sz = size(err2{iln}); 259 | 260 | if isequal(sz, [nx 2]) 261 | lo{iln} = lineval - err2{iln}(:,1)'; 262 | hi{iln} = lineval + err2{iln}(:,2)'; 263 | elseif isequal(sz, [nx 1]) 264 | lo{iln} = lineval - err2{iln}'; 265 | hi{iln} = lineval + err2{iln}'; 266 | elseif isequal(sz, [1 2]) 267 | lo{iln} = lineval - err2{iln}(1); 268 | hi{iln} = lineval + err2{iln}(2); 269 | elseif isequal(sz, [1 1]) 270 | lo{iln} = lineval - err2{iln}; 271 | hi{iln} = lineval + err2{iln}; 272 | elseif isequal(sz, [2 nx]) % not documented, but accepted anyways 273 | lo{iln} = lineval - err2{iln}(:,1); 274 | hi{iln} = lineval + err2{iln}(:,2); 275 | elseif isequal(sz, [1 nx]) % not documented, but accepted anyways 276 | lo{iln} = lineval - err2{iln}; 277 | hi{iln} = lineval + err2{iln}; 278 | elseif isequal(sz, [2 1]) % not documented, but accepted anyways 279 | lo{iln} = lineval - err2{iln}(1); 280 | hi{iln} = lineval + err2{iln}(2); 281 | else 282 | error('Error bounds must be npt x nside x nline array'); 283 | end 284 | 285 | end 286 | 287 | % Combine all data (xline, yline, marker, linestyle, color, lower bound 288 | % (x or y), upper bound (x or y) 289 | 290 | plotdata = [plotdata; linedata lo hi]; 291 | 292 | end 293 | close(htemp); 294 | 295 | % Override colormap 296 | 297 | if hascmap 298 | nd = size(plotdata,1); 299 | cmap = repmat(cmap, ceil(nd/size(cmap,1)), 1); 300 | cmap = cmap(1:nd,:); 301 | plotdata(:,5) = num2cell(cmap,2); 302 | end 303 | 304 | 305 | %-------------------- 306 | % Plot 307 | %-------------------- 308 | 309 | % Setup of x and y, plus line and patch properties 310 | 311 | nline = size(plotdata,1); 312 | [xl, yl, xp, yp, marker, lnsty, lncol, ptchcol, alpha] = deal(cell(nline,1)); 313 | 314 | for iln = 1:nline 315 | xl{iln} = plotdata{iln,1}; 316 | yl{iln} = plotdata{iln,2}; 317 | % if isvert 318 | % xp{iln} = [plotdata{iln,1} fliplr(plotdata{iln,1})]; 319 | % yp{iln} = [plotdata{iln,6} fliplr(plotdata{iln,7})]; 320 | % else 321 | % xp{iln} = [plotdata{iln,6} fliplr(plotdata{iln,7})]; 322 | % yp{iln} = [plotdata{iln,2} fliplr(plotdata{iln,2})]; 323 | % end 324 | 325 | [xp{iln}, yp{iln}] = calcpatch(plotdata{iln,1}, plotdata{iln,2}, isvert, plotdata{iln,6}, plotdata{iln,7}, nanflag); 326 | 327 | marker{iln} = plotdata{iln,3}; 328 | lnsty{iln} = plotdata{iln,4}; 329 | 330 | if usealpha 331 | lncol{iln} = plotdata{iln,5}; 332 | ptchcol{iln} = plotdata{iln,5}; 333 | alpha{iln} = trans; 334 | else 335 | lncol{iln} = plotdata{iln,5}; 336 | ptchcol{iln} = interp1([0 1], [1 1 1; lncol{iln}], trans); 337 | alpha{iln} = 1; 338 | end 339 | end 340 | 341 | % Plot patches and lines 342 | 343 | if verLessThan('matlab', '8.4.0') 344 | [hp,hl] = deal(zeros(nline,1)); 345 | else 346 | [hp,hl] = deal(gobjects(nline,1)); 347 | end 348 | 349 | 350 | for iln = 1:nline 351 | hp(iln) = patch(xp{iln}, yp{iln}, ptchcol{iln}, 'facealpha', alpha{iln}, 'edgecolor', 'none', 'parent', hax); 352 | end 353 | 354 | for iln = 1:nline 355 | hl(iln) = line(xl{iln}, yl{iln}, 'marker', marker{iln}, 'linestyle', lnsty{iln}, 'color', lncol{iln}, 'parent', hax); 356 | end 357 | 358 | %-------------------- 359 | % Assign output 360 | %-------------------- 361 | 362 | nargoutchk(0,2); 363 | 364 | if nargout >= 1 365 | varargout{1} = hl; 366 | end 367 | 368 | if nargout == 2 369 | varargout{2} = hp; 370 | end 371 | 372 | %-------------------- 373 | % Parse optional 374 | % parameters 375 | %-------------------- 376 | 377 | function [found, val, vars] = parseparam(vars, param) 378 | 379 | isvar = cellfun(@(x) ischar(x) && strcmpi(x, param), vars); 380 | 381 | if sum(isvar) > 1 382 | error('Parameters can only be passed once'); 383 | end 384 | 385 | if any(isvar) 386 | found = true; 387 | idx = find(isvar); 388 | val = vars{idx+1}; 389 | vars([idx idx+1]) = []; 390 | else 391 | found = false; 392 | val = []; 393 | end 394 | 395 | %---------------------------- 396 | % Calculate patch coordinates 397 | %---------------------------- 398 | 399 | function [xp, yp] = calcpatch(xl, yl, isvert, lo, hi, nanflag) 400 | 401 | ismissing = isnan([xl;yl;lo;hi]); 402 | 403 | % If gap method, split 404 | 405 | if any(ismissing(:)) && strcmp(nanflag, 'gap') 406 | 407 | tmp = [xl;yl;lo;hi]; 408 | 409 | idx = find(any(ismissing,1)); 410 | n = diff([0 idx length(xl)]); 411 | 412 | tmp = mat2cell(tmp, 4, n); 413 | isemp = cellfun('isempty', tmp); 414 | tmp = tmp(~isemp); 415 | 416 | tmp = cellfun(@(a) a(:,~any(isnan(a),1)), tmp, 'uni', 0); 417 | isemp = cellfun('isempty', tmp); 418 | tmp = tmp(~isemp); 419 | 420 | xl = cellfun(@(a) a(1,:), tmp, 'uni', 0); 421 | yl = cellfun(@(a) a(2,:), tmp, 'uni', 0); 422 | lo = cellfun(@(a) a(3,:), tmp, 'uni', 0); 423 | hi = cellfun(@(a) a(4,:), tmp, 'uni', 0); 424 | else 425 | xl = {xl}; 426 | yl = {yl}; 427 | lo = {lo}; 428 | hi = {hi}; 429 | end 430 | 431 | [xp, yp] = deal(cell(size(xl))); 432 | 433 | for ii = 1:length(xl) 434 | 435 | iseq = ~verLessThan('matlab', '8.4.0') && isequal(lo{ii}, hi{ii}); % deal with zero-width bug in R2014b/R2015a 436 | 437 | if isvert 438 | if iseq 439 | xp{ii} = [xl{ii} nan(size(xl{ii}))]; 440 | yp{ii} = [lo{ii} fliplr(hi{ii})]; 441 | else 442 | xp{ii} = [xl{ii} fliplr(xl{ii})]; 443 | yp{ii} = [lo{ii} fliplr(hi{ii})]; 444 | end 445 | else 446 | if iseq 447 | xp{ii} = [lo{ii} fliplr(hi{ii})]; 448 | yp{ii} = [yl{ii} nan(size(yl{ii}))]; 449 | else 450 | xp{ii} = [lo{ii} fliplr(hi{ii})]; 451 | yp{ii} = [yl{ii} fliplr(yl{ii})]; 452 | end 453 | end 454 | 455 | if strcmp(nanflag, 'fill') 456 | xp{ii} = inpaint_nans(xp{ii}', 4); 457 | yp{ii} = inpaint_nans(yp{ii}', 4); 458 | if iseq % need to maintain NaNs for zero-width bug 459 | nx = length(xp{ii}); 460 | xp{ii}((nx/2)+1:end) = NaN; 461 | end 462 | elseif strcmp(nanflag, 'remove') 463 | if iseq 464 | nx = length(xp{ii}); 465 | keepnan = false(size(xp)); 466 | keepnan((nx/2)+1:end) = true; 467 | isn = (isnan(xp{ii}) | isnan(yp{ii})) & ~keepnan; 468 | else 469 | isn = isnan(xp{ii}) | isnan(yp{ii}); 470 | end 471 | xp{ii} = xp{ii}(~isn); 472 | yp{ii} = yp{ii}(~isn); 473 | end 474 | 475 | end 476 | 477 | if strcmp(nanflag, 'gap') 478 | [xp, yp] = singlepatch(xp, yp); 479 | else 480 | xp = xp{1}; 481 | yp = yp{1}; 482 | end 483 | 484 | -------------------------------------------------------------------------------- /inpaint_nans.m: -------------------------------------------------------------------------------- 1 | function B=inpaint_nans(A,method) % INPAINT_NANS: in-paints over nans in an array % usage: B=INPAINT_NANS(A) % default method % usage: B=INPAINT_NANS(A,method) % specify method used % % Solves approximation to one of several pdes to % interpolate and extrapolate holes in an array % % arguments (input): % A - nxm array with some NaNs to be filled in % % method - (OPTIONAL) scalar numeric flag - specifies % which approach (or physical metaphor to use % for the interpolation.) All methods are capable % of extrapolation, some are better than others. % There are also speed differences, as well as % accuracy differences for smooth surfaces. % % methods {0,1,2} use a simple plate metaphor. % method 3 uses a better plate equation, % but may be much slower and uses % more memory. % method 4 uses a spring metaphor. % method 5 is an 8 neighbor average, with no % rationale behind it compared to the % other methods. I do not recommend % its use. % % method == 0 --> (DEFAULT) see method 1, but % this method does not build as large of a % linear system in the case of only a few % NaNs in a large array. % Extrapolation behavior is linear. % % method == 1 --> simple approach, applies del^2 % over the entire array, then drops those parts % of the array which do not have any contact with % NaNs. Uses a least squares approach, but it % does not modify known values. % In the case of small arrays, this method is % quite fast as it does very little extra work. % Extrapolation behavior is linear. % % method == 2 --> uses del^2, but solving a direct % linear system of equations for nan elements. % This method will be the fastest possible for % large systems since it uses the sparsest % possible system of equations. Not a least % squares approach, so it may be least robust % to noise on the boundaries of any holes. % This method will also be least able to % interpolate accurately for smooth surfaces. % Extrapolation behavior is linear. % % Note: method 2 has problems in 1-d, so this % method is disabled for vector inputs. % % method == 3 --+ See method 0, but uses del^4 for % the interpolating operator. This may result % in more accurate interpolations, at some cost % in speed. % % method == 4 --+ Uses a spring metaphor. Assumes % springs (with a nominal length of zero) % connect each node with every neighbor % (horizontally, vertically and diagonally) % Since each node tries to be like its neighbors, % extrapolation is as a constant function where % this is consistent with the neighboring nodes. % % method == 5 --+ See method 2, but use an average % of the 8 nearest neighbors to any element. % This method is NOT recommended for use. % % % arguments (output): % B - nxm array with NaNs replaced % % % Example: % [x,y] = meshgrid(0:.01:1); % z0 = exp(x+y); % znan = z0; % znan(20:50,40:70) = NaN; % znan(30:90,5:10) = NaN; % znan(70:75,40:90) = NaN; % % z = inpaint_nans(znan); % % % See also: griddata, interp1 % % Author: John D'Errico % e-mail address: woodchips@rochester.rr.com % Release: 2 % Release date: 4/15/06 % I always need to know which elements are NaN, % and what size the array is for any method [n,m]=size(A); A=A(:); nm=n*m; k=isnan(A(:)); % list the nodes which are known, and which will % be interpolated nan_list=find(k); known_list=find(~k); % how many nans overall nan_count=length(nan_list); % convert NaN indices to (r,c) form % nan_list==find(k) are the unrolled (linear) indices % (row,column) form [nr,nc]=ind2sub([n,m],nan_list); % both forms of index in one array: % column 1 == unrolled index % column 2 == row index % column 3 == column index nan_list=[nan_list,nr,nc]; % supply default method if (nargin<2) || isempty(method) method = 0; elseif ~ismember(method,0:5) error 'If supplied, method must be one of: {0,1,2,3,4,5}.' end % for different methods switch method case 0 % The same as method == 1, except only work on those % elements which are NaN, or at least touch a NaN. % is it 1-d or 2-d? if (m == 1) || (n == 1) % really a 1-d case work_list = nan_list(:,1); work_list = unique([work_list;work_list - 1;work_list + 1]); work_list(work_list <= 1) = []; work_list(work_list >= nm) = []; nw = numel(work_list); u = (1:nw)'; fda = sparse(repmat(u,1,3),bsxfun(@plus,work_list,-1:1), ... repmat([1 -2 1],nw,1),nw,nm); else % a 2-d case % horizontal and vertical neighbors only talks_to = [-1 0;0 -1;1 0;0 1]; neighbors_list=identify_neighbors(n,m,nan_list,talks_to); % list of all nodes we have identified all_list=[nan_list;neighbors_list]; % generate sparse array with second partials on row % variable for each element in either list, but only % for those nodes which have a row index > 1 or < n L = find((all_list(:,2) > 1) & (all_list(:,2) < n)); nl=length(L); if nl>0 fda=sparse(repmat(all_list(L,1),1,3), ... repmat(all_list(L,1),1,3)+repmat([-1 0 1],nl,1), ... repmat([1 -2 1],nl,1),nm,nm); else fda=spalloc(n*m,n*m,size(all_list,1)*5); end % 2nd partials on column index L = find((all_list(:,3) > 1) & (all_list(:,3) < m)); nl=length(L); if nl>0 fda=fda+sparse(repmat(all_list(L,1),1,3), ... repmat(all_list(L,1),1,3)+repmat([-n 0 n],nl,1), ... repmat([1 -2 1],nl,1),nm,nm); end end % eliminate knowns rhs=-fda(:,known_list)*A(known_list); k=find(any(fda(:,nan_list(:,1)),2)); % and solve... B=A; B(nan_list(:,1))=fda(k,nan_list(:,1))\rhs(k); case 1 % least squares approach with del^2. Build system % for every array element as an unknown, and then % eliminate those which are knowns. % Build sparse matrix approximating del^2 for % every element in A. % is it 1-d or 2-d? if (m == 1) || (n == 1) % a 1-d case u = (1:(nm-2))'; fda = sparse(repmat(u,1,3),bsxfun(@plus,u,0:2), ... repmat([1 -2 1],nm-2,1),nm-2,nm); else % a 2-d case % Compute finite difference for second partials % on row variable first [i,j]=ndgrid(2:(n-1),1:m); ind=i(:)+(j(:)-1)*n; np=(n-2)*m; fda=sparse(repmat(ind,1,3),[ind-1,ind,ind+1], ... repmat([1 -2 1],np,1),n*m,n*m); % now second partials on column variable [i,j]=ndgrid(1:n,2:(m-1)); ind=i(:)+(j(:)-1)*n; np=n*(m-2); fda=fda+sparse(repmat(ind,1,3),[ind-n,ind,ind+n], ... repmat([1 -2 1],np,1),nm,nm); end % eliminate knowns rhs=-fda(:,known_list)*A(known_list); k=find(any(fda(:,nan_list),2)); % and solve... B=A; B(nan_list(:,1))=fda(k,nan_list(:,1))\rhs(k); case 2 % Direct solve for del^2 BVP across holes % generate sparse array with second partials on row % variable for each nan element, only for those nodes % which have a row index > 1 or < n % is it 1-d or 2-d? if (m == 1) || (n == 1) % really just a 1-d case error('Method 2 has problems for vector input. Please use another method.') else % a 2-d case L = find((nan_list(:,2) > 1) & (nan_list(:,2) < n)); nl=length(L); if nl>0 fda=sparse(repmat(nan_list(L,1),1,3), ... repmat(nan_list(L,1),1,3)+repmat([-1 0 1],nl,1), ... repmat([1 -2 1],nl,1),n*m,n*m); else fda=spalloc(n*m,n*m,size(nan_list,1)*5); end % 2nd partials on column index L = find((nan_list(:,3) > 1) & (nan_list(:,3) < m)); nl=length(L); if nl>0 fda=fda+sparse(repmat(nan_list(L,1),1,3), ... repmat(nan_list(L,1),1,3)+repmat([-n 0 n],nl,1), ... repmat([1 -2 1],nl,1),n*m,n*m); end % fix boundary conditions at extreme corners % of the array in case there were nans there if ismember(1,nan_list(:,1)) fda(1,[1 2 n+1])=[-2 1 1]; end if ismember(n,nan_list(:,1)) fda(n,[n, n-1,n+n])=[-2 1 1]; end if ismember(nm-n+1,nan_list(:,1)) fda(nm-n+1,[nm-n+1,nm-n+2,nm-n])=[-2 1 1]; end if ismember(nm,nan_list(:,1)) fda(nm,[nm,nm-1,nm-n])=[-2 1 1]; end % eliminate knowns rhs=-fda(:,known_list)*A(known_list); % and solve... B=A; k=nan_list(:,1); B(k)=fda(k,k)\rhs(k); end case 3 % The same as method == 0, except uses del^4 as the % interpolating operator. % del^4 template of neighbors talks_to = [-2 0;-1 -1;-1 0;-1 1;0 -2;0 -1; ... 0 1;0 2;1 -1;1 0;1 1;2 0]; neighbors_list=identify_neighbors(n,m,nan_list,talks_to); % list of all nodes we have identified all_list=[nan_list;neighbors_list]; % generate sparse array with del^4, but only % for those nodes which have a row & column index % >= 3 or <= n-2 L = find( (all_list(:,2) >= 3) & ... (all_list(:,2) <= (n-2)) & ... (all_list(:,3) >= 3) & ... (all_list(:,3) <= (m-2))); nl=length(L); if nl>0 % do the entire template at once fda=sparse(repmat(all_list(L,1),1,13), ... repmat(all_list(L,1),1,13) + ... repmat([-2*n,-n-1,-n,-n+1,-2,-1,0,1,2,n-1,n,n+1,2*n],nl,1), ... repmat([1 2 -8 2 1 -8 20 -8 1 2 -8 2 1],nl,1),nm,nm); else fda=spalloc(n*m,n*m,size(all_list,1)*5); end % on the boundaries, reduce the order around the edges L = find((((all_list(:,2) == 2) | ... (all_list(:,2) == (n-1))) & ... (all_list(:,3) >= 2) & ... (all_list(:,3) <= (m-1))) | ... (((all_list(:,3) == 2) | ... (all_list(:,3) == (m-1))) & ... (all_list(:,2) >= 2) & ... (all_list(:,2) <= (n-1)))); nl=length(L); if nl>0 fda=fda+sparse(repmat(all_list(L,1),1,5), ... repmat(all_list(L,1),1,5) + ... repmat([-n,-1,0,+1,n],nl,1), ... repmat([1 1 -4 1 1],nl,1),nm,nm); end L = find( ((all_list(:,2) == 1) | ... (all_list(:,2) == n)) & ... (all_list(:,3) >= 2) & ... (all_list(:,3) <= (m-1))); nl=length(L); if nl>0 fda=fda+sparse(repmat(all_list(L,1),1,3), ... repmat(all_list(L,1),1,3) + ... repmat([-n,0,n],nl,1), ... repmat([1 -2 1],nl,1),nm,nm); end L = find( ((all_list(:,3) == 1) | ... (all_list(:,3) == m)) & ... (all_list(:,2) >= 2) & ... (all_list(:,2) <= (n-1))); nl=length(L); if nl>0 fda=fda+sparse(repmat(all_list(L,1),1,3), ... repmat(all_list(L,1),1,3) + ... repmat([-1,0,1],nl,1), ... repmat([1 -2 1],nl,1),nm,nm); end % eliminate knowns rhs=-fda(:,known_list)*A(known_list); k=find(any(fda(:,nan_list(:,1)),2)); % and solve... B=A; B(nan_list(:,1))=fda(k,nan_list(:,1))\rhs(k); case 4 % Spring analogy % interpolating operator. % list of all springs between a node and a horizontal % or vertical neighbor hv_list=[-1 -1 0;1 1 0;-n 0 -1;n 0 1]; hv_springs=[]; for i=1:4 hvs=nan_list+repmat(hv_list(i,:),nan_count,1); k=(hvs(:,2)>=1) & (hvs(:,2)<=n) & (hvs(:,3)>=1) & (hvs(:,3)<=m); hv_springs=[hv_springs;[nan_list(k,1),hvs(k,1)]]; end % delete replicate springs hv_springs=unique(sort(hv_springs,2),'rows'); % build sparse matrix of connections, springs % connecting diagonal neighbors are weaker than % the horizontal and vertical springs nhv=size(hv_springs,1); springs=sparse(repmat((1:nhv)',1,2),hv_springs, ... repmat([1 -1],nhv,1),nhv,nm); % eliminate knowns rhs=-springs(:,known_list)*A(known_list); % and solve... B=A; B(nan_list(:,1))=springs(:,nan_list(:,1))\rhs; case 5 % Average of 8 nearest neighbors % generate sparse array to average 8 nearest neighbors % for each nan element, be careful around edges fda=spalloc(n*m,n*m,size(nan_list,1)*9); % -1,-1 L = find((nan_list(:,2) > 1) & (nan_list(:,3) > 1)); nl=length(L); if nl>0 fda=fda+sparse(repmat(nan_list(L,1),1,2), ... repmat(nan_list(L,1),1,2)+repmat([-n-1, 0],nl,1), ... repmat([1 -1],nl,1),n*m,n*m); end % 0,-1 L = find(nan_list(:,3) > 1); nl=length(L); if nl>0 fda=fda+sparse(repmat(nan_list(L,1),1,2), ... repmat(nan_list(L,1),1,2)+repmat([-n, 0],nl,1), ... repmat([1 -1],nl,1),n*m,n*m); end % +1,-1 L = find((nan_list(:,2) < n) & (nan_list(:,3) > 1)); nl=length(L); if nl>0 fda=fda+sparse(repmat(nan_list(L,1),1,2), ... repmat(nan_list(L,1),1,2)+repmat([-n+1, 0],nl,1), ... repmat([1 -1],nl,1),n*m,n*m); end % -1,0 L = find(nan_list(:,2) > 1); nl=length(L); if nl>0 fda=fda+sparse(repmat(nan_list(L,1),1,2), ... repmat(nan_list(L,1),1,2)+repmat([-1, 0],nl,1), ... repmat([1 -1],nl,1),n*m,n*m); end % +1,0 L = find(nan_list(:,2) < n); nl=length(L); if nl>0 fda=fda+sparse(repmat(nan_list(L,1),1,2), ... repmat(nan_list(L,1),1,2)+repmat([1, 0],nl,1), ... repmat([1 -1],nl,1),n*m,n*m); end % -1,+1 L = find((nan_list(:,2) > 1) & (nan_list(:,3) < m)); nl=length(L); if nl>0 fda=fda+sparse(repmat(nan_list(L,1),1,2), ... repmat(nan_list(L,1),1,2)+repmat([n-1, 0],nl,1), ... repmat([1 -1],nl,1),n*m,n*m); end % 0,+1 L = find(nan_list(:,3) < m); nl=length(L); if nl>0 fda=fda+sparse(repmat(nan_list(L,1),1,2), ... repmat(nan_list(L,1),1,2)+repmat([n, 0],nl,1), ... repmat([1 -1],nl,1),n*m,n*m); end % +1,+1 L = find((nan_list(:,2) < n) & (nan_list(:,3) < m)); nl=length(L); if nl>0 fda=fda+sparse(repmat(nan_list(L,1),1,2), ... repmat(nan_list(L,1),1,2)+repmat([n+1, 0],nl,1), ... repmat([1 -1],nl,1),n*m,n*m); end % eliminate knowns rhs=-fda(:,known_list)*A(known_list); % and solve... B=A; k=nan_list(:,1); B(k)=fda(k,k)\rhs(k); end % all done, make sure that B is the same shape as % A was when we came in. B=reshape(B,n,m); % ==================================================== % end of main function % ==================================================== % ==================================================== % begin subfunctions % ==================================================== function neighbors_list=identify_neighbors(n,m,nan_list,talks_to) % identify_neighbors: identifies all the neighbors of % those nodes in nan_list, not including the nans % themselves % % arguments (input): % n,m - scalar - [n,m]=size(A), where A is the % array to be interpolated % nan_list - array - list of every nan element in A % nan_list(i,1) == linear index of i'th nan element % nan_list(i,2) == row index of i'th nan element % nan_list(i,3) == column index of i'th nan element % talks_to - px2 array - defines which nodes communicate % with each other, i.e., which nodes are neighbors. % % talks_to(i,1) - defines the offset in the row % dimension of a neighbor % talks_to(i,2) - defines the offset in the column % dimension of a neighbor % % For example, talks_to = [-1 0;0 -1;1 0;0 1] % means that each node talks only to its immediate % neighbors horizontally and vertically. % % arguments(output): % neighbors_list - array - list of all neighbors of % all the nodes in nan_list if ~isempty(nan_list) % use the definition of a neighbor in talks_to nan_count=size(nan_list,1); talk_count=size(talks_to,1); nn=zeros(nan_count*talk_count,2); j=[1,nan_count]; for i=1:talk_count nn(j(1):j(2),:)=nan_list(:,2:3) + ... repmat(talks_to(i,:),nan_count,1); j=j+nan_count; end % drop those nodes which fall outside the bounds of the % original array L = (nn(:,1)<1)|(nn(:,1)>n)|(nn(:,2)<1)|(nn(:,2)>m); nn(L,:)=[]; % form the same format 3 column array as nan_list neighbors_list=[sub2ind([n,m],nn(:,1),nn(:,2)),nn]; % delete replicates in the neighbors list neighbors_list=unique(neighbors_list,'rows'); % and delete those which are also in the list of NaNs. neighbors_list=setdiff(neighbors_list,nan_list,'rows'); else neighbors_list=[]; end --------------------------------------------------------------------------------