├── .gitignore ├── DESCRIPTION ├── LICENSE ├── NAMESPACE ├── R ├── Get_Plot_geneExpressionvsMidline.R ├── ObjectToCoords.R ├── analyzeLayer.R ├── boxplots.R ├── boxplotsRaw.R ├── highlightCells_highlightCluster.R ├── plotUMAP1_inSItu.R ├── plotUMAP1_inSituMidline.R └── plotUMAP1_line_and_histogram.R ├── README.md └── man ├── XeniumBoxPlot.Rd ├── XeniumBoxPlotRaw.Rd ├── analyzeLayer.Rd ├── getExpressionvsMidline.Rd ├── getUMAP1_MidlineData.Rd ├── highlightCells.Rd ├── highlightCluster.Rd ├── object_FOV_to_coordinates.Rd ├── plotGeneExpressionVsMidline.Rd ├── plotUMAP1_inSitu.Rd ├── plotUMAP1_inSituMidline.Rd └── plotUMAP1_vsMidline.Rd /.gitignore: -------------------------------------------------------------------------------- 1 | # History files 2 | .Rhistory 3 | .Rapp.history 4 | 5 | # Session Data files 6 | .RData 7 | .RDataTmp 8 | 9 | # User-specific files 10 | .Ruserdata 11 | 12 | # Example code in package build process 13 | *-Ex.R 14 | 15 | # Output files from R CMD build 16 | /*.tar.gz 17 | 18 | # Output files from R CMD check 19 | /*.Rcheck/ 20 | 21 | # RStudio files 22 | .Rproj.user/ 23 | 24 | # produced vignettes 25 | vignettes/*.html 26 | vignettes/*.pdf 27 | 28 | # OAuth2 token, see https://github.com/hadley/httr/releases/tag/v0.3 29 | .httr-oauth 30 | 31 | # knitr and R markdown default cache directories 32 | *_cache/ 33 | /cache/ 34 | 35 | # Temporary files created by R markdown 36 | *.utf8.md 37 | *.knit.md 38 | 39 | # R Environment Variables 40 | .Renviron 41 | 42 | # pkgdown site 43 | docs/ 44 | 45 | # translation temp files 46 | po/*~ 47 | 48 | # RStudio Connect folder 49 | rsconnect/ 50 | -------------------------------------------------------------------------------- /DESCRIPTION: -------------------------------------------------------------------------------- 1 | Package: XeniumSpatialAnalysis 2 | Type: Package 3 | Title: Xenium Spatial Analysis Toolkit 4 | Version: 0.1.0 5 | Author: Margarita Kapustina 6 | Maintainer: Margarita Kapustina 7 | Description: Toolkit in R to perform spatial analysis of Xenium single-cell spatial transcriptomic data, and assess spatial gradients of gene expression. 8 | Imports: 9 | Seurat, 10 | SeuratObject, 11 | ggplot2, 12 | cowplot, 13 | dplyr, 14 | tibble, 15 | gridExtra, 16 | viridis, 17 | tibble 18 | License: GPL-3.0 file LICENSE 19 | Encoding: UTF-8 20 | RoxygenNote: 7.2.3 21 | Depends: 22 | R (>= 4.3.2) 23 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /NAMESPACE: -------------------------------------------------------------------------------- 1 | exportPattern("^[[:alpha:]]+") 2 | -------------------------------------------------------------------------------- /R/Get_Plot_geneExpressionvsMidline.R: -------------------------------------------------------------------------------- 1 | #' Generate Gene Expression values for cells and their distance away from Spatial Midline 2 | #' @author Margarita Kapustina 3 | #' 4 | #' @description Generates gene expression data away from the computed spatial midline. Supports one FOV at a time. 5 | #' 6 | #' @param object a Xenium object 7 | #' @param genes Character vector of gene names to fetch expression data for 8 | #' @param FOV FOV to extract coordinates from 9 | #' @param degs User-defined angle, defined in degrees, that intersects centrepoint in midline computation 10 | #' 11 | #' @return Dataframe containing Cell IDs, coordinates (X,Y), gene expression counts for specififed gene(s), 12 | #' and computed distance away from spatial midline of each cell, in specified FOV. 13 | #' @import ggplot2 14 | #' @import Seurat 15 | #' @import SeuratObject 16 | #' @import tibble 17 | #' 18 | #' @examples 19 | #' geneExpression_X1fov <- getExpressionvsMidline(AD, FOV = "X1fov", genes = c("Epha1", "Cntnap4", 'Pcp4', 'Slc17a7'), degs = 45) 20 | #' geneExpression_fov <- getExpressionvsMidline(AD, FOV = "fov", genes = c("Epha1", "Cntnap4", 'Pcp4', 'Slc17a7'), degs = 50) 21 | #' @export 22 | 23 | getExpressionvsMidline <- function(object, FOV = 'fov', genes, degs) { 24 | 25 | #get current default assay to then set back later 26 | current_default_assay = SeuratObject::DefaultAssay(object) 27 | 28 | # Set default FOV 29 | SeuratObject::DefaultFOV(object) <- FOV 30 | 31 | # Get tissue coordinates for the set default FOV only 32 | df <- Seurat::GetTissueCoordinates(object) 33 | 34 | # Fetch gene expression data 35 | exp <- Seurat::FetchData(object, genes, layer = 'counts') 36 | exp = tibble::rownames_to_column(exp, var = "cell") 37 | 38 | # Get expression values for only FOV of interest 39 | cells_to_get <- df[["cell"]] 40 | exp_subset <- exp[exp$cell %in% cells_to_get, ] 41 | 42 | # Make an expression and coordinate table 43 | expressionTable_fov <- cbind(exp_subset, df) 44 | expressionTable_fov <- expressionTable_fov[, !duplicated(colnames(expressionTable_fov))] 45 | 46 | # Change column names to capitals (works for all number of genes) 47 | names(expressionTable_fov)[names(expressionTable_fov) == 'x'] <- 'X' 48 | names(expressionTable_fov)[names(expressionTable_fov) == 'y'] <- 'Y' 49 | 50 | #store .getCentre() Function 51 | .getCentre <- function(df,doMean=F){ 52 | if(doMean){ 53 | dX <- mean(df$X) 54 | dY <- mean(df$Y) 55 | }else{ 56 | dX <- median(df$X) 57 | dY <- median(df$Y) 58 | } 59 | 60 | dX <- df$X-dX 61 | dY <- df$Y-dY 62 | 63 | df <- cbind(df,dX,dY) 64 | 65 | return(df) 66 | } 67 | 68 | # Compute center of coordinates used for analysis (i.e., per FOV) 69 | centre = expressionTable_fov %>% .getCentre(doMean = FALSE) 70 | # Run distance operation, make a new data frame "myplot" 71 | rads <- degs * pi / 180 72 | m <- tan(rads) 73 | dLine <- (-m * centre$dX + centre$dY) / sqrt(1 + m^2) 74 | myplot <- cbind(centre, dLine) 75 | #plot out UMAP_1 embedding with nucleus midline 76 | gg <- ggplot2::ggplot(myplot,ggplot2::aes(x=dX,y=dY)) 77 | gg <- gg + ggplot2::geom_point() 78 | gg <- gg + ggplot2::geom_abline(slope=m) 79 | gg <- gg + ggplot2::coord_fixed() + ggplot2::ggtitle('Midline of Cells') 80 | print(gg) 81 | 82 | # Annotate which FOV the data belongs to 83 | myplot$fov = FOV 84 | # make myplot df unique for each fov ########## 85 | # Creating a new object with a dynamic name 86 | dfTitle <- "geneExpression" 87 | dfName <- paste(dfTitle, FOV, sep = "_") 88 | assign(dfName, myplot) 89 | 90 | print("If spatial midline looks off, please adjust 'degs' parameter!") 91 | message('Please save the dataframe with FOV identifier, such as: ', 92 | dfName) 93 | 94 | #change default assay back to whatever it was before running function 95 | SeuratObject::DefaultAssay(object) <- current_default_assay 96 | 97 | # Return the resulting df (for the FOV specified) 98 | return(get(dfName)) 99 | } 100 | 101 | #' Plots gene expression data away from the computed spatial midline (supports multiple FOVs) 102 | #' @author Margarita Kapustina 103 | #' 104 | #' @description Plot gene expression data for cells and their distance away from Spatial Midline across multiple FOVs. 105 | #' 106 | #' @param geneExpressionData List of dataframes generated with getExpressionvsMidline() across multiple FOVs 107 | #' @param genes Character vector of gene names to their plot expression data 108 | #' @param binNumber Number of bins to use for histogram (bars). Suggested binNumber = total # cells in pooled data/binwidth 109 | #' @param binwidth Width of bins for gene expression averaging (lines) Suggested binwidth = 40 for 40micron bins 110 | #' @param save_plot Option to save plot as .eps in working directory (TRUE, FALSE) 111 | #' @param xlim Define x-axis limits as vector. 112 | #' 113 | #' @return Pooled dataframe containing Cell IDs, coordinates (X,Y), gene expression counts for specififed gene(s), 114 | #' and computed distance away from spatial midline of each cell, across all FOVs in data 115 | #' 116 | #' @import ggplot2 117 | #' @import Seurat 118 | #' @import tibble 119 | #' 120 | #' @examples 121 | #' pooled_GeneExpression = plotGeneExpressionVsMidline(geneExpressionData = list(geneExpression_fov, geneExpression_fX1ov), genes = c("Epha1", "Cntnap4", 'Pcp4', 'Slc17a7')) 122 | #' @export 123 | 124 | 125 | plotGeneExpressionVsMidline <- function(geneExpressionData, genes, 126 | binNumber = 7, binwidth = 40, 127 | save_plot = FALSE, 128 | xlim = c(-500, 500)) { 129 | # Use do.call to rbind the objects in the list 130 | pooled_df <- do.call(rbind, geneExpressionData) 131 | #create plot 132 | p1 <- ggplot2::ggplot(pooled_df, ggplot2::aes(x = dLine)) + 133 | ggplot2::geom_histogram(ggplot2::aes(y = ggplot2::after_stat(count / 100)), bins = binNumber, boundary = 0, colour = 'blue') + 134 | ggplot2::coord_cartesian(ylim = c(0, 5), xlim = xlim) + 135 | ggplot2::xlab('Distance away from spatial midline') + 136 | ggplot2::ylab(paste('Bars: Number of cells per binned distance/100 \n Lines: Gene expression averaged per bin for genes:\n', paste(genes, collapse = ", "))) + 137 | ggplot2::ggtitle('Average gene expression values vs Spatial midline') 138 | 139 | # Define a color palette (adjust as needed) 140 | color_palette <- c("red", "green", "pink", "black", "orange") 141 | # Create a mapping from gene names to colors 142 | gene_color_mapping <- setNames(color_palette[1:length(genes)], genes) 143 | cat("Colour Scheme (Gene : colour):\n") 144 | # Add lines for each gene in genes with consistent colors 145 | for (gene in genes) { 146 | gene_color <- gene_color_mapping[[gene]] 147 | p1 <- p1 + ggplot2::stat_summary_bin(ggplot2::aes(x = dLine, y = .data[[gene]]), fun = mean, binwidth = binwidth, geom = 'line', colour = gene_color, linewidth = 1.1) 148 | # Print message indicating which gene and color are being used 149 | cat(gene, ":", gene_color, "\n")} 150 | print("Generating plot...") 151 | print(p1) 152 | 153 | # Calculate the number of cells in each bin 154 | bin_counts <- ggplot2::ggplot_build(p1)$data[[1]] 155 | cat("Number of cells in each bin (note: plotted are counts/100): 156 | Beginning on left-most bin...\n") 157 | for (i in seq_along(bin_counts$y)) { 158 | cat("Bin:", i, ", Count:", bin_counts$count[i], "\n") 159 | } 160 | # saving 161 | if (save_plot) { 162 | print('Saving plot...') 163 | dev.copy2eps(file = 'BoxPlots_SCTcounts.eps') 164 | print("Plot saved to working directory.")} 165 | 166 | return(pooled_df) 167 | } 168 | -------------------------------------------------------------------------------- /R/ObjectToCoords.R: -------------------------------------------------------------------------------- 1 | #' Get cell coordinates from a Xenium object 2 | #' @author Margarita Kapustina 3 | #' 4 | #' @description Create a dataframe with cell ID coordinates and cluster IDs from a Xenium Seurat object. 5 | #' 6 | #' @param object Xenium object 7 | #' @param thisFOV Name of your FOV to extract coordinates from 8 | #' @param threshold_y Boundary that you define to compute cells that are above and below this boundary 9 | #' @param angle_adjust Option to adjust the angle of your cell coordinates (TRUE, FALSE) 10 | #' @param thetda_deg Specify the degrees you wish to rotate your slice by 11 | #' @param flip_x_coordinates Option to flip your slice in the horizontal plane (TRUE, FALSE) 12 | #' 13 | #' @return A dataframe with cell ID, coordinates, cluster ID, and a plot with UMAP_1 embedding values and the boundary that you define 14 | #' 15 | #' @import ggplot2 16 | #' @import Seurat 17 | #' @import SeuratObject 18 | #' @import cowplot 19 | #' 20 | #' @examples 21 | #' distanceData_X2fov = object_FOV_to_coordinates(object = a, thisFOV = 'X2fov', threshold_y = 2694, angle_adjust = FALSE, theta_deg = 0, flip_x_coordinates = FALSE) 22 | #' @export 23 | object_FOV_to_coordinates <- function(object = object, thisFOV = 'X9fov', threshold_y = 2000, angle_adjust = FALSE, theta_deg = 0, flip_x_coordinates = FALSE) { 24 | #set default fov 25 | #################################### 26 | DefaultFOV(object) = thisFOV 27 | #get current default assay to then set back later 28 | current_default_assay = SeuratObject::DefaultAssay(object) 29 | # Get UMAP_1 embeddings from Xenium object 30 | cell.embeddings <- object@reductions$umap@cell.embeddings 31 | cell.embeddings.df <- as.data.frame(cell.embeddings) 32 | names(cell.embeddings.df)[1] <- "UMAP_1" 33 | # Add Cell_ID as a column 34 | cell.embeddings.df <- tibble::rownames_to_column(cell.embeddings.df, "Cell_ID") 35 | #rename cell.embeddings.df as df for plotting use 36 | df = cell.embeddings.df 37 | # Add a column 'y' with default value 0 38 | df$y <- 0 39 | # Get tissue coordinates (for slice Xfov3) 40 | df <- Seurat::GetTissueCoordinates(object) 41 | # Merge tissue coordinates (FOV of interest only) with the dataframe containing 1D UMAP embeddings 42 | cells_to_get <- df[["cell"]] 43 | cell.embeddings.df_subset <- cell.embeddings.df[cell.embeddings.df$Cell_ID %in% cells_to_get, ] 44 | names(cell.embeddings.df_subset)[1] <- 'cell' 45 | coordsEmbeddingsdf_oneFOV <- merge(df, cell.embeddings.df_subset, by = 'cell') 46 | # Angle of rotation in degrees if angle_adjust is TRUE 47 | if (angle_adjust) { 48 | theta_rad <- theta_deg * (pi / 180) #deg to radians 49 | newX <- coordsEmbeddingsdf_oneFOV$x * cos(theta_rad) + coordsEmbeddingsdf_oneFOV$y * sin(theta_rad) 50 | newY <- -coordsEmbeddingsdf_oneFOV$x * sin(theta_rad) + coordsEmbeddingsdf_oneFOV$y * cos(theta_rad) 51 | coordsEmbeddingsdf_oneFOV$x <- newX 52 | coordsEmbeddingsdf_oneFOV$y = newY 53 | } 54 | 55 | if (flip_x_coordinates) { 56 | # Calculate the maximum x-coordinate value 57 | max_x <- max(coordsEmbeddingsdf_oneFOV$x) 58 | coordsEmbeddingsdf_oneFOV$x <- max_x - coordsEmbeddingsdf_oneFOV$x # Flip the x-coordinates 59 | } 60 | ############################## 61 | # Define the threshold x-value for each indv. fov ##############change for each FOV! 62 | #add what the threshold was 63 | coordsEmbeddingsdf_oneFOV$threshold <- threshold_y 64 | # Create a new column based on the condition 65 | coordsEmbeddingsdf_oneFOV$above_or_below <- ifelse(coordsEmbeddingsdf_oneFOV$y > threshold_y, "Above", "Below") 66 | #add what the fov was 67 | coordsEmbeddingsdf_oneFOV$fov = thisFOV 68 | 69 | message('Your threshold is...') 70 | print(threshold_y) 71 | 72 | message('Plotting your final coordinates, UMAP_1 embeddings and threshold...') 73 | #plot out UMAP_1 embedding with nucleus midline#################### 74 | gg <- ggplot(coordsEmbeddingsdf_oneFOV,aes(x=x,y=y)) 75 | gg <- gg + geom_point(aes(colour= UMAP_1), size = 1.5) 76 | gg <- gg + geom_hline(yintercept = threshold_y, linetype = "dashed", color = "red") 77 | gg <- gg + coord_fixed() +ggtitle(thisFOV,threshold_y)+scale_color_viridis_c() 78 | print(gg) ##############print for each FOV! 79 | # Construct the file name dynamically based on the value of this 80 | message('Plot will be saved as:') 81 | file_name <- paste(thisFOV, ".eps", sep = "") 82 | print(file_name) 83 | ggsave(file_name, gg) 84 | # Extract cluster IDs from Seurat object ########### 85 | cluster_ids <- as.data.frame(Idents(object)) 86 | colnames(cluster_ids) <- c("cluster_ids") # Renaming column for better readability 87 | cluster_ids$cell <- rownames(cluster_ids)# Adding cell ID column based on row names 88 | rownames(cluster_ids) = NULL 89 | #View(cluster_ids) 90 | # Merge cluster IDs into the existing dataframe based on cell IDs############ 91 | merged_data <- merge(coordsEmbeddingsdf_oneFOV, cluster_ids, by = "cell", all.x = TRUE) 92 | #name dataframe with unique identifier, and eename the object dynamically 93 | uniqueFOV_name <- paste("distanceData_", thisFOV, sep = "") 94 | assign(uniqueFOV_name, merged_data) 95 | ############ 96 | message('Name this file as:') 97 | print(uniqueFOV_name) 98 | return(merged_data) 99 | ################################# END ANALYSIS HERE 100 | } 101 | -------------------------------------------------------------------------------- /R/analyzeLayer.R: -------------------------------------------------------------------------------- 1 | #' Calculate the normalized distance along a cortical layer and away from a boundary for cell coordinates 2 | #' @author Margarita Kapustina 3 | #' 4 | #' @description Compute distance along a cortical layer, and the distance away from a defined boundary, using distance data computed with object_FOV_to_coordinates function. Note: please save your data computed object_FOV_to_coordinates named as 'distance_data_all'. If you have multiple FOVs run through the object_FOV_to_coordinates function, merge them and rename the object. If you have only a single FOV, simply rename the object. 5 | #' 6 | #' @param thisFOV Name of your FOV to define your boundary within 7 | #' 8 | #' @return A dataframe with cell ID, coordinates, cluster ID, normalized distance along a cortical layer, and distance away from your defined boundary. 9 | #' 10 | #' @import ggplot2 11 | #' @import Seurat 12 | #' @import SeuratObject 13 | #' @import cowplot 14 | #' 15 | #' @examples 16 | #' #before running the function, please save merged FOV-specific data computed with the object_FOV_to_coordinates function, as distance_data_all 17 | #' #example: distance_data_all = rbind(distanceData_X2fov, distanceData_X3fov) 18 | #' #Please noe that distance_data_all needs to be within your environment to run analyzeLayer 19 | #' cellDataX3fov <- analyzeLayer("X3fov") 20 | #' @export 21 | 22 | #run on distance data all (get from objectToCoords function) 23 | analyzeLayer<- function(thisFOV) { 24 | #select FOV to get subset of data 25 | cell_data <- distance_data_all[distance_data_all$fov == thisFOV, ] 26 | 27 | # Plot out the cells in space 28 | # ggplot(cell_data, aes(x = x, y = y)) + 29 | # geom_point()+ 30 | # coord_cartesian(ylim = c(0, 5000), xlim = c(0,5000))+ 31 | # coord_fixed() # Set y-axis limits 32 | 33 | #doesnt mattter if aspect ratio is off, continue as normal the plot will re-adjust later when using ggplot2 34 | par(pty="s") 35 | plot(cell_data$x, cell_data$y, type = "p", xlab = "x", ylab = "y") 36 | 37 | message('Please click around 25-30 points to set major landmarks in plot screen...') 38 | message('When done hit ESC on keyboard.') 39 | 40 | # Prompt user to click points on the plot 41 | points <- locator() #hit ESC when done on keyboard 42 | 43 | # Extract x and y coordinates of clicked points 44 | x_coords <- points$x 45 | y_coords <- points$y 46 | points = as.data.frame(points) 47 | 48 | # ggplot(points, aes(x = x, y = y)) + 49 | # geom_point(color = "blue")+ 50 | # coord_cartesian(ylim = c(0, 5000), xlim = c(0,5000)) + 51 | #geom_point(data = cell_data, aes(x = x, y = y), color = "red") + coord_fixed() 52 | 53 | # Plot the points and create the path 54 | # ggplot(points, aes(x = x, y = y)) + 55 | # geom_path(color = "blue") + 56 | # geom_point(color = "red") 57 | 58 | 59 | ######### analysis v1 60 | # Function to calculate the spline between neighboring points 61 | calculate_spline <- function(x1, y1, x2, y2, n_points = 100) { 62 | # Generate x values for the spline 63 | x_values <- seq(x1, x2, length.out = n_points) 64 | 65 | # Interpolate y values using a spline 66 | y_values <- spline(x = c(x1, x2), y = c(y1, y2), xout = x_values)$y 67 | 68 | return(data.frame(x = x_values, y = y_values)) 69 | } 70 | 71 | # Initialize a list to store spline coordinates 72 | spline_segments <- list() 73 | 74 | # Iterate through points and calculate spline segments 75 | for (i in 1:(nrow(points) - 1)) { 76 | # Calculate spline segment between neighboring points 77 | segment <- calculate_spline(points$x[i], points$y[i], points$x[i + 1], points$y[i + 1]) 78 | spline_segments[[i]] <- segment 79 | } 80 | 81 | # Combine spline segments into a single DataFrame 82 | spline_data <- do.call(rbind, spline_segments) 83 | 84 | # Plot the spline data 85 | # plot(spline_data$x, spline_data$y, type = "p", 86 | # col = "blue", xlab = "X", ylab = "Y", main = "Spline Through Nearest Points") 87 | 88 | project_point_onto_segment = function(px, py, x1, y1, x2, y2) { 89 | dx <- x2 - x1 90 | dy <- y2 - y1 91 | t <- ((px - x1) * dx + (py - y1) * dy) / (dx^2 + dy^2) 92 | t <- pmax(0, pmin(1, t)) 93 | projected_x <- x1 + t * dx 94 | projected_y <- y1 + t * dy 95 | return(c(projected_x, projected_y)) 96 | } 97 | # Initialize a vector to store the distances 98 | distances <- numeric(nrow(cell_data)) 99 | 100 | # Plot the cell_data points, spline_data, and distances 101 | par(pty="s") 102 | plot(cell_data$x, cell_data$y, type = "p", col = "red", pch = 16, xlab = "X", ylab = "Y", main = "Cell coordinates, estimated L6b boundary and Distance to boundary") 103 | points(spline_data$x, spline_data$y, col = "blue") 104 | for (i in 1:nrow(cell_data)) { 105 | nearest_index <- which.min((spline_data$x - cell_data$x[i])^2 + (spline_data$y - cell_data$y[i])^2) 106 | if (nearest_index == 1) { 107 | projected <- project_point_onto_segment(cell_data$x[i], cell_data$y[i], 108 | spline_data$x[1], spline_data$y[1], 109 | spline_data$x[2], spline_data$y[2]) 110 | } else if (nearest_index == nrow(spline_data)) { 111 | projected <- project_point_onto_segment(cell_data$x[i], cell_data$y[i], 112 | spline_data$x[nrow(spline_data) - 1], spline_data$y[nrow(spline_data) - 1], 113 | spline_data$x[nrow(spline_data)], spline_data$y[nrow(spline_data)]) 114 | } else { 115 | projected <- project_point_onto_segment(cell_data$x[i], cell_data$y[i], 116 | spline_data$x[nearest_index - 1], spline_data$y[nearest_index - 1], 117 | spline_data$x[nearest_index], spline_data$y[nearest_index]) 118 | } 119 | segments(projected[1], projected[2], cell_data$x[i], cell_data$y[i], col = "green") # Reversed the order of endpoints 120 | 121 | # Calculate the distance between the point and its projection onto the spline 122 | distances[i] <- sqrt((cell_data$x[i] - projected[1])^2 + (cell_data$y[i] - projected[2])^2) 123 | } 124 | 125 | # Add the distances as a new column in the cell_data dataframe 126 | cell_data$distance_to_spline <- distances 127 | 128 | # Add a legend 129 | legend("bottomright", legend = c("Cell Data", "Spline Data", "Distance to Spline"), col = c("red", "blue", "green"), pch = c(16, NA, NA), lty = c(NA, 1, 1)) 130 | #plot: colour by distance away 131 | # Define the color palette using colorRampPalette 132 | my_palette <- colorRampPalette(c("magenta", "green"))(nrow(cell_data)) 133 | 134 | # plot(cell_data$x, cell_data$y, 135 | # col = my_palette[cell_data$distance_to_spline], pch = 16, xlab = "X", ylab = "Y", 136 | # main = "Color-coded Cells by Distance to Spline") 137 | 138 | ##normalize the distance to spline stored in cell_data$distance_to_spline 139 | cell_data$normalized_distance_to_spline <- (cell_data$distance_to_spline - min(cell_data$distance_to_spline)) / (max(cell_data$distance_to_spline) - min(cell_data$distance_to_spline)) 140 | 141 | ##ggplot version 142 | title <- paste0("Color-coded Cells by Normalized Distance to Spline: ", thisFOV) 143 | p2 = ggplot(cell_data, aes(x = x, y = y)) + 144 | geom_point(aes(color = normalized_distance_to_spline), pch = 16) + 145 | scale_color_viridis_c() + 146 | labs(x = "X", y = "Y", title = title) + 147 | coord_fixed() 148 | # Define the file path and name for saving dynamically 149 | filename <- paste0("distancetoSpline_", thisFOV, ".eps") 150 | ggsave(filename = filename, plot = p2) 151 | ##or## as factor 152 | # plot(cell_data$x, cell_data$y, 153 | # col = my_palette[factor(cell_data$distance_to_spline)], pch = 16, xlab = "X", ylab = "Y", 154 | # main = "Color-coded Cells by Distance to Spline") 155 | 156 | ############### end here for now 157 | 158 | # Initialize vector to store distances 159 | cell_data$distance_along_spline <- numeric(nrow(cell_data)) 160 | 161 | # Iterate over each point in cell_data 162 | for (i in 1:nrow(cell_data)) { 163 | # Calculate distances to all points in spline_data 164 | distances <- sqrt((spline_data$x - cell_data$x[i])^2 + (spline_data$y - cell_data$y[i])^2) 165 | 166 | # Find index of nearest point 167 | nearest_index <- which.min(distances) 168 | 169 | # Calculate distance along spline to nearest point 170 | if (nearest_index == 1) { 171 | # If nearest point is the first point in spline_data, set distance to 0 172 | cell_data$distance_along_spline[i] <- 0 173 | } else { 174 | # Calculate distance as sum of distances between consecutive points along the spline up to the nearest point 175 | cell_data$distance_along_spline[i] <- sum(sqrt(diff(spline_data$x[1:nearest_index])^2 + diff(spline_data$y[1:nearest_index])^2)) 176 | } 177 | } 178 | 179 | ##normalize the distance to spline stored in cell_data$distance_along_spline 180 | cell_data$normalized_distance_along_spline <- (cell_data$distance_along_spline - min(cell_data$distance_along_spline)) / (max(cell_data$distance_along_spline) - min(cell_data$distance_along_spline)) 181 | 182 | # Plot histogram of distances along the spline for all cells 183 | # Create the ggplot histogram 184 | title = paste0("Histogram of distances along the spline: ", thisFOV) 185 | p_hist <- ggplot(cell_data, aes(x = distance_along_spline)) + 186 | geom_histogram(color = "grey", fill = "violet", bins = 30) + 187 | labs(x = "Distance along spline", y = "Frequency", title = title) 188 | filename <- paste0("Histogram_distanceAlongSpline_", thisFOV, ".eps") 189 | ggsave(filename = filename, plot = p_hist) 190 | 191 | # Create the ggplot histogram 192 | title = paste0("Histogram of distances to the spline: ", thisFOV) 193 | p_hist2 <- ggplot(cell_data, aes(x = distance_to_spline)) + 194 | geom_histogram(color = "grey", fill = "gold", bins = 30) + 195 | labs(x = "Distance to spline", y = "Frequency", title = title) 196 | filename <- paste0("Histogram_distanceToSpline_", thisFOV, ".eps") 197 | ggsave(filename = filename, plot = p_hist2) 198 | ############################### 199 | 200 | # Color-code the cells by their distance along the spline 201 | my_palette <- colorRampPalette(c("red", "blue"))(nrow(cell_data)) 202 | title <- paste0("Color-coded Cells by Normalized Distance Along Spline: ", thisFOV) 203 | p3 = ggplot(cell_data, aes(x = x, y = y)) + 204 | geom_point(aes(color = normalized_distance_along_spline), pch = 16) + 205 | scale_color_gradientn(colors = my_palette) + 206 | labs(x = "X", y = "Y", title = title)+ coord_fixed() 207 | filename <- paste0("distanceAlongSpline_", thisFOV, ".eps") 208 | ggsave(filename = filename, plot = p3) 209 | 210 | #name dataframe with unique identifier, and name the object dynamically 211 | uniqueFOV_name <- paste("cellData", thisFOV, sep = "") 212 | assign(uniqueFOV_name, cell_data) 213 | ############ 214 | message('Name this file as:') 215 | print(uniqueFOV_name) 216 | # Return the modified cell_data dataframe 217 | return(cell_data) 218 | } 219 | -------------------------------------------------------------------------------- /R/boxplots.R: -------------------------------------------------------------------------------- 1 | #' Xenium Gene Box Plots 2 | #' @author Margarita Kapustina 3 | #' 4 | #' @description Create a boxplot denoting expression of gene(s) of interest across all clusters. SCT assay counts (sequencing depth corrected counts) are used. 5 | #' 6 | #' @param object Xenium object 7 | #' @param genes Character vector of gene names to plot 8 | #' @param ncol Specify number of columns in plot 9 | #' @param save_plot Option to save plot as .eps in working directory (TRUE, FALSE) 10 | #' 11 | #' @return A grid of boxplots showing gene expression across all clusters 12 | #' 13 | #' @import ggplot2 14 | #' @import Seurat 15 | #' @import SeuratObject 16 | #' @import cowplot 17 | #' 18 | #' @examples 19 | #' #create vector ie my_genes= c('C1ql2','Slc17a7', 'Gng13') 20 | #' BoxPlots = XeniumBoxPlot(object = atn, genes = my_genes) 21 | #' @export 22 | 23 | XeniumBoxPlot = function(object, genes, ncol = 4, save_plot = FALSE) { 24 | 25 | #get current default assay to then set back later 26 | current_default_assay = SeuratObject::DefaultAssay(object) 27 | #set default assay to SCT 28 | SeuratObject::DefaultAssay(object) = 'SCT' 29 | 30 | suppressWarnings({ #supresses following warning: 31 | # Computation failed in `stat_ydensity()` 32 | #Caused by error in `density.default()`: 33 | # ! 'bw' is not positive. 34 | 35 | # Function to generate a customized violin plot 36 | Plot = function(theGenes) { 37 | Seurat::VlnPlot(object, features = theGenes, pt.size = 0, layer = "counts", adjust = 0) + 38 | theme(legend.position = "nolegend") + 39 | ggplot2:: geom_boxplot(outlier.size = 0, outlier.stroke = 0, lwd = 0.3) + 40 | ggplot2:: theme(axis.line = element_line(linewidth = 0.5), axis.text.y = element_text(size = 6), axis.text.x = element_text(size = 6), 41 | axis.title = element_blank(), title = element_text(size = 6), 42 | plot.background = element_blank()) 43 | } 44 | print("Generating boxplots...") 45 | 46 | # Initialize an empty ist to store individual plots 47 | plots <- list() 48 | 49 | # Loop through the gene vector and call the Plot function for each gene 50 | for (i in seq_along(genes)) { 51 | plotName <- paste0("p", i) 52 | assign(plotName, Plot(theGenes = genes[i])) 53 | 54 | # Add the plot to the list 55 | plots[[i]] <- get(plotName) 56 | } 57 | 58 | # Create a grid of plots using plot_grid 59 | grid <- cowplot::plot_grid(plotlist = plots, ncol = ncol) 60 | 61 | # Print the combined plot 62 | print(grid) 63 | 64 | # saving 65 | if (save_plot) { 66 | print('Saving plot...') 67 | dev.copy2eps(file = 'BoxPlots_SCTcounts.eps') 68 | print("Plot saved to working directory.")} 69 | }) 70 | 71 | #change default assay back to whatever it was before running function 72 | SeuratObject::DefaultAssay(object) <- current_default_assay 73 | 74 | # Return the combined plot 75 | return(grid) 76 | } 77 | -------------------------------------------------------------------------------- /R/boxplotsRaw.R: -------------------------------------------------------------------------------- 1 | #' Xenium Gene Box Plots - Raw Counts 2 | #' @author Margarita Kapustina 3 | #' 4 | #' @description Create a boxplot denoting expression of gene(s) of interest across all clusters. Xenium assay counts (raw counts) are used. 5 | #' 6 | #' @param object Xenium object 7 | #' @param genes Character vector of gene names to plot 8 | #' @param ncol Specify number of columns in plot 9 | #' @param save_plot Option to save plot as .eps in working directory (TRUE, FALSE) 10 | #' 11 | #' @return A grid of boxplots showing raw gene expression across all clusters 12 | #' 13 | #' @import ggplot2 14 | #' @import Seurat 15 | #' @import SeuratObject 16 | #' @import cowplot 17 | #' 18 | #' @examples 19 | #' #create vector ie my_genes= c('C1ql2','Slc17a7', 'Gng13') 20 | #' BoxPlots = XeniumBoxPlotRaw(object = atn, genes = my_genes) 21 | #' @export 22 | 23 | XeniumBoxPlotRaw = function(object, genes, ncol = 4, save_plot = FALSE) { 24 | 25 | #get current default assay to then set back later 26 | current_default_assay = SeuratObject::DefaultAssay(object) 27 | #change default assay to Xenium, otherwise default is SCT 28 | SeuratObject::DefaultAssay(object) <- "Xenium" 29 | 30 | suppressWarnings({ #supresses following warning: 31 | # Computation failed in `stat_ydensity()` 32 | #Caused by error in `density.default()`: 33 | # ! 'bw' is not positive. 34 | 35 | # Function to generate a customized violin plot 36 | Plot = function(theGenes) { 37 | Seurat::VlnPlot(object, features = theGenes, pt.size = 0, layer = "counts", adjust = 0) + 38 | ggplot2::theme(legend.position = "nolegend") + 39 | ggplot2::geom_boxplot(outlier.size = 0, outlier.stroke = 0, lwd = 0.3) + 40 | ggplot2::theme(axis.line = element_line(linewidth = 0.5), axis.text.y = element_text(size = 6), axis.text.x = element_text(size = 6), 41 | axis.title = element_blank(), title = element_text(size = 6), 42 | plot.background = element_blank()) 43 | } 44 | print('Generating boxplots...') 45 | 46 | # Initialize an empty list to store individual plots 47 | plots <- list() 48 | 49 | # Loop through the gene vector and call the Plot function for each gene 50 | for (i in seq_along(genes)) { 51 | plotName <- paste0("p", i) 52 | assign(plotName, Plot(theGenes = genes[i])) 53 | 54 | # Add the plot to the list 55 | plots[[i]] <- get(plotName) 56 | } 57 | 58 | # Create a grid of plots using plot_grid 59 | grid <- cowplot::plot_grid(plotlist = plots, ncol = ncol) 60 | 61 | # Print the combined plot 62 | print(grid) 63 | 64 | # saving 65 | if (save_plot) { 66 | print('Saving plot...') 67 | dev.copy2eps(file = 'BoxPlots_rawCounts.eps') 68 | print("Plot saved to working directory.")} 69 | 70 | #change default assay back to whatever it was before running function 71 | SeuratObject::DefaultAssay(object) <- current_default_assay 72 | 73 | # Return the combined plot 74 | return(grid) 75 | 76 | # Code that may produce warnings 77 | }) 78 | } 79 | -------------------------------------------------------------------------------- /R/highlightCells_highlightCluster.R: -------------------------------------------------------------------------------- 1 | #' Highlight Cluster 2 | #' @author Margarita Kapustina 3 | #' 4 | #' @description Highlight cluster(s) of choice in a Xenium object, within specified FOV(s). Cluster of interest labelled "this cluster" 5 | #' and all other clusters labelled "all else". Supported for clusters within the same object. 6 | #' 7 | #' @param object a Xenium object 8 | #' @param cluster_id Identity of cluster to highlight 9 | #' @param size Size of the highlighted cells in the plot 10 | #' @param alpha_value Alpha (transparency) value of the highlighted cells 11 | #' @param FOV Character vector specifying the fields of view (FOVs) to use for plotting 12 | #' @param color_palette Color palette for the plot 13 | #' @param save_plot Option to save plot as .pdf in working directory (TRUE, FALSE) 14 | #' 15 | #' 16 | #' @return An ImageDimPlot with highlighted cells. 17 | #' 18 | #' @examples 19 | #' highlightCluster(object = obj, cluster_id = 1, FOV = c('X1fov', 'X2fov')) 20 | #' 21 | #' @import Seurat 22 | #' @export 23 | highlightCluster <- function(object, cluster_id, size = 3, alpha_value = 0.5, FOV = c("fov", 'X1fov'), color_palette = 'glasbey', save_plot = FALSE) { 24 | toPlot <- Seurat::WhichCells(object, idents = cluster_id) 25 | object@meta.data$ClusterToPlot <- ifelse(colnames(object) %in% toPlot, "this_cluster", "all_else") 26 | p1 = Seurat::ImageDimPlot(object, group.by = 'ClusterToPlot', size = size, alpha = alpha_value, fov = FOV, cols = color_palette) 27 | # saving plot 28 | if (save_plot) { 29 | print('Saving plot...') 30 | dev.copy2pdf(file = 'ClusterHighlighted.pdf') 31 | print("Plot saved to working directory.")} 32 | print(p1) 33 | #remove the ClusterToPlot column 34 | object@meta.data$ClusterToPlot = NULL 35 | return(p1) 36 | } 37 | 38 | 39 | #' Highlight Cells 40 | #' @author Margarita Kapustina 41 | #' 42 | #' @description Highlight cells from a Xenium object of interest, within another Xenium object, within specified FOV(s). 43 | #' Cells from object of interest labelled as "these cells", other cells labelled as "all else". Suggested to use when subsetting Xenium objects. 44 | #' 45 | #' @param highlight_obj Xenium object that you want to highlight cells from, labelled: "these cells" 46 | #' @param within_obj Xenium object that you want show highlighted cells within, labelled: "all else" (ie cells plotted but not highlighted) 47 | #' @param size Size of the highlighted cells in the plot 48 | #' @param alpha_value Alpha (transparency) value of the highlighted cells 49 | #' @param FOV Character vector specifying the fields of view (FOVs) to use for plotting 50 | #' @param color_palette Color palette for the plot 51 | #' @param save_plot Option to save plot as .pdf in working directory (TRUE, FALSE) 52 | #' 53 | #' @return An ImageDimPlot with highlighted cells. 54 | #' 55 | #' @examples 56 | #' highlightCells(highlight_obj = atn, within_obj = ExcitatoryNeurons, FOV = c('X1fov')) 57 | #' 58 | #' @import Seurat 59 | #' @export 60 | 61 | #highlight cells from one Xenium object, in another (ie good to use for subsets) 62 | #object 1 is the cluster that you want to highlight - ie "these cells" 63 | #object 2 will show you where its cells are plotted, but will not highlight them - ie "all else" cells. 64 | highlightCells <- function(highlight_obj, within_obj, size = 3, 65 | alpha_value = 0.5, FOV = c("fov", 'X1fov'), 66 | color_palette = 'glasbey', 67 | save_plot = FALSE) { 68 | toPlot <- Seurat::WhichCells(highlight_obj) 69 | within_obj@meta.data$CellsToPlot <- ifelse(colnames(within_obj) %in% toPlot, "these_cells", "all_else") 70 | p1 = Seurat::ImageDimPlot(within_obj, group.by = 'CellsToPlot', size = size, alpha = alpha_value, fov = FOV, cols = color_palette) 71 | # saving plot 72 | if (save_plot) { 73 | print('Saving plot...') 74 | dev.copy2pdf(file = 'ObjectHighlighted.pdf') 75 | print("Plot saved to working directory.")} 76 | print(p1) 77 | #remove the ClusterToPlot column 78 | within_obj@meta.data$CellsToPlot = NULL 79 | return(p1) 80 | } 81 | -------------------------------------------------------------------------------- /R/plotUMAP1_inSItu.R: -------------------------------------------------------------------------------- 1 | #' Xenium 1-D UMAP Embeddings Plotted in Situ 2 | #' @author Margarita Kapustina 3 | #' 4 | #' @description Plots: 1-dimensional UMAP, the corresponding histogram of UMAP_1 embedding values, and 5 | #' the UMAP_1 embedding values in situ within specified FOV. 6 | #' 7 | #' @param object a Xenium object 8 | #' @param FOV FOV to plot UMAP_1 embedding values in 9 | #' @param EmbeddingsPlotTitle title for UMAP_1 Embeddings plot 10 | #' @param HistogramPlotTitle title for UMAP_1 Embeddings Histogram plot 11 | #' @param inSituPlotTitle title for UMAP_1 Embeddings in situ plot 12 | #' @param save_plot Option to save plot as .eps in working directory (TRUE, FALSE) 13 | #' 14 | #' @return Dataframe containing Cell IDs, coordinates (X,Y) and computed UMAP_1 embeddings values for cells in specified FOV. 15 | #' #Plots 1D UMAP, corresponding histogram of UMAP_1 embeddings values, and UMAP_1 embeddings values plotted _in situ_. 16 | #' 17 | #' @import ggplot2 18 | #' @import gridExtra 19 | #' @import viridis 20 | #' @import Seurat 21 | #' @import SeuratObject 22 | #' @import tibble 23 | #' 24 | #' @examples 25 | #' AD_df = plotUMAP1_inSitu(object = AD, FOV = 'fov') 26 | #' @export 27 | 28 | plotUMAP1_inSitu <- function(object, 29 | FOV = 'X1fov', 30 | EmbeddingsPlotTitle = 'UMAP_1 embedding for all\n cells in subset', 31 | HistogramPlotTitle = 'Distribution of UMAP_1 embedding\n values all cells in subset', 32 | inSituPlotTitle = 'UMAP_1 embedding in situ\n for representative FOV', 33 | save_plot = FALSE){ 34 | 35 | #get current default assay to then set back later 36 | current_default_assay = SeuratObject::DefaultAssay(object) 37 | 38 | # Get UMAP_1 embeddings from Xenium object 39 | cell.embeddings <- object@reductions$umap@cell.embeddings 40 | cell.embeddings.df <- as.data.frame(cell.embeddings) 41 | names(cell.embeddings.df)[1] <- "UMAP_1" 42 | 43 | # Add Cell_ID as a column 44 | cell.embeddings.df <- tibble::rownames_to_column(cell.embeddings.df, "Cell_ID") 45 | #rename cell.embeddings.df as df for plotting use 46 | df = cell.embeddings.df 47 | # Add a column 'y' with default value 0 48 | df$y <- 0 49 | 50 | # Plot continuum of UMAP_1 embedding values 51 | embeddings_plot <- ggplot2::ggplot(df, ggplot2::aes(x = UMAP_1, y = y, colour = UMAP_1)) + 52 | ggplot2::geom_point(size = 0.5) + 53 | ggplot2::theme(axis.text.y = ggplot2::element_blank(), 54 | axis.ticks.y = ggplot2::element_blank()) + 55 | ggplot2::scale_color_viridis_c() + 56 | ggplot2::geom_jitter() + 57 | ggplot2::ggtitle(EmbeddingsPlotTitle) 58 | 59 | #histogram of embeddings across all slices 60 | embeddings_histogram = ggplot2::ggplot(df, ggplot2::aes(x = UMAP_1, fill = ggplot2::after_stat(x))) + 61 | ggplot2::geom_histogram(bins = 40, colour = 'blue')+ 62 | ggplot2::ggtitle(HistogramPlotTitle)+ 63 | viridis::scale_fill_viridis() 64 | #set default FOV to fetch coordinates for FOV of choice 65 | SeuratObject::DefaultFOV(object) <- FOV 66 | 67 | # Get tissue coordinates 68 | df <- Seurat::GetTissueCoordinates(object) 69 | 70 | # Merge tissue coordinates (FOV of interest only) with the dataframe containing 1D UMAP embeddings 71 | cells_to_get <- df[["cell"]] 72 | cell.embeddings.df_subset <- cell.embeddings.df[cell.embeddings.df$Cell_ID %in% cells_to_get, ] 73 | names(cell.embeddings.df_subset)[1] <- 'cell' 74 | coordsEmbeddingsdf <- merge(df, cell.embeddings.df_subset, by = 'cell') 75 | 76 | # Plot embedding values by colour in FOV of choice 77 | inSituPlot = ggplot2::ggplot(coordsEmbeddingsdf, ggplot2::aes(x = x, y = y)) + 78 | ggplot2::geom_point(ggplot2::aes(colour = UMAP_1), size = 1.5) + 79 | ggplot2::scale_color_viridis_c()+ 80 | ggplot2::ggtitle(inSituPlotTitle) + 81 | ggplot2::coord_fixed() 82 | # Print the plots side by side 83 | # print(embeddings_plot|embeddings_histogram|inSituPlot) 84 | print("Generating plots...") 85 | gridExtra::grid.arrange( 86 | gridExtra::arrangeGrob(embeddings_plot, embeddings_histogram, ncol = 1), 87 | inSituPlot, 88 | ncol = 2, 89 | widths = c(0.5, 0.5) 90 | ) 91 | # saving plot 92 | if (save_plot) { 93 | print('Saving plot...') 94 | dev.copy2eps(file = 'UMAP1EmbeddingsInSitu_plot.eps') 95 | print("Plot saved to working directory.")} 96 | 97 | #change default assay back to whatever it was before running function 98 | SeuratObject::DefaultAssay(object) <- current_default_assay 99 | 100 | # Return the merged data frame 101 | return(coordsEmbeddingsdf) 102 | } 103 | 104 | -------------------------------------------------------------------------------- /R/plotUMAP1_inSituMidline.R: -------------------------------------------------------------------------------- 1 | #' Xenium 1-D UMAP Embeddings Plotted in Situ 2 | #' @author Margarita Kapustina 3 | #' 4 | #' @description Plots: 1-dimensional UMAP, the corresponding histogram of UMAP_1 embedding values, 5 | #' the UMAP_1 embedding values in situ within specified FOV, and computes the midline intersecting cells within specified FOV. 6 | #' note: please compute UMAP_1 embeddings beforehand. 7 | #' 8 | #' @param object a Xenium object 9 | #' @param FOV FOV to plot UMAP_1 embedding values in 10 | #' @param degs User-defined angle, defined in degrees, that intersects centrepoint in midline computation 11 | #' @param EmbeddingsPlotTitle title for UMAP_1 Embeddings plot 12 | #' @param HistogramPlotTitle title for UMAP_1 Embeddings Histogram plot 13 | #' @param inSituPlotTitle title for UMAP_1 Embeddings in situ plot 14 | #' @param save_plot Option to save plot as .eps in working directory (TRUE, FALSE) 15 | #' 16 | #' @return Dataframe containing Cell IDs, coordinates (X,Y) and computed UMAP_1 embeddings values for cells in specified FOV. 17 | #' #Plots 1D UMAP, corresponding histogram of UMAP_1 embeddings values, and UMAP_1 embeddings values plotted in situ. 18 | #' 19 | #' @import ggplot2 20 | #' @import gridExtra 21 | #' @import viridis 22 | #' @import Seurat 23 | #' @import SeuratObject 24 | #' @import tibble 25 | #' 26 | #' @examples 27 | #' AD_df = plotUMAP1_inSituMidline(object = AD, FOV = 'fov', degs = 65) 28 | #' @export 29 | 30 | plotUMAP1_inSituMidline <- function(object, 31 | FOV = 'X1fov', 32 | degs = 45, 33 | save_plot = FALSE, 34 | EmbeddingsPlotTitle = 'UMAP_1 embedding for all\n cells in subset', 35 | HistogramPlotTitle = 'Distribution of UMAP_1 embedding\n values all cells in subset', 36 | inSituPlotTitle = 'UMAP_1 embedding in situ\n for representative FOV'){ 37 | #store .getCentre() Function 38 | .getCentre <- function(df,doMean=F){ 39 | if(doMean){ 40 | dX <- mean(df$X) 41 | dY <- mean(df$Y) 42 | }else{ 43 | dX <- median(df$X) 44 | dY <- median(df$Y) 45 | } 46 | 47 | dX <- df$X-dX 48 | dY <- df$Y-dY 49 | 50 | df <- cbind(df,dX,dY) 51 | 52 | return(df) 53 | } 54 | 55 | #get current default assay to then set back later 56 | current_default_assay = SeuratObject::DefaultAssay(object) 57 | 58 | # Get UMAP_1 embeddings from Xenium object 59 | cell.embeddings <- object@reductions$umap@cell.embeddings 60 | cell.embeddings.df <- as.data.frame(cell.embeddings) 61 | names(cell.embeddings.df)[1] <- "UMAP_1" 62 | 63 | # Add Cell_ID as a column 64 | cell.embeddings.df <- tibble::rownames_to_column(cell.embeddings.df, "Cell_ID") 65 | #rename cell.embeddings.df as df for plotting use 66 | df = cell.embeddings.df 67 | # Add a column 'y' with default value 0 68 | df$y <- 0 69 | 70 | # Plot continuum of UMAP_1 embedding values 71 | embeddings_plot <- ggplot2::ggplot(df, ggplot2::aes(x = UMAP_1, y = y, colour = UMAP_1)) + 72 | ggplot2::geom_point(size = 0.5) + 73 | ggplot2::theme(axis.text.y = ggplot2::element_blank(), 74 | axis.ticks.y = ggplot2::element_blank()) + 75 | ggplot2::scale_color_viridis_c() + 76 | ggplot2::geom_jitter() + 77 | ggplot2::ggtitle(EmbeddingsPlotTitle) 78 | 79 | #histogram of embeddings across all slices 80 | embeddings_histogram = ggplot2::ggplot(df, ggplot2::aes(x = UMAP_1, fill = ggplot2::after_stat(x))) + 81 | ggplot2::geom_histogram(bins = 40, colour = 'blue')+ 82 | ggplot2::ggtitle(HistogramPlotTitle)+ 83 | viridis::scale_fill_viridis() 84 | #set default FOV to fetch coordinates for FOV of choice 85 | SeuratObject::DefaultFOV(object) <- FOV 86 | 87 | # Get tissue coordinates 88 | df <- Seurat::GetTissueCoordinates(object) 89 | 90 | # Merge tissue coordinates (FOV of interest only) with the dataframe containing 1D UMAP embeddings 91 | cells_to_get <- df[["cell"]] 92 | cell.embeddings.df_subset <- cell.embeddings.df[cell.embeddings.df$Cell_ID %in% cells_to_get, ] 93 | names(cell.embeddings.df_subset)[1] <- 'cell' 94 | coordsEmbeddingsdf <- merge(df, cell.embeddings.df_subset, by = 'cell') 95 | 96 | # Plot embedding values by colour in FOV of choice 97 | inSituPlot = ggplot2::ggplot(coordsEmbeddingsdf, ggplot2::aes(x = x, y = y)) + 98 | ggplot2::geom_point(ggplot2::aes(colour = UMAP_1), size = 1.5) + 99 | ggplot2::scale_color_viridis_c()+ 100 | ggplot2::ggtitle(inSituPlotTitle) + 101 | ggplot2::coord_fixed() 102 | 103 | #plot UMAP_1 embedding with nucleus midline as well###################### 104 | names(coordsEmbeddingsdf)[names(coordsEmbeddingsdf) == 'x'] <- 'X' 105 | names(coordsEmbeddingsdf)[names(coordsEmbeddingsdf) == 'y'] <- 'Y' 106 | 107 | #compute the centre of the coordinates 108 | centre = coordsEmbeddingsdf %>% .getCentre(doMean = TRUE) 109 | #set slope of line 110 | degs = degs 111 | rads <- degs*pi/180 112 | m <- tan(rads) 113 | #run distance operation and make new df "myplot" 114 | dLine <- (-m*centre$dX+centre$dY)/sqrt(1+m^2) 115 | myplot <- cbind(centre,dLine) 116 | #plot out UMAP_1 embedding with nucleus midline 117 | gg <- ggplot2::ggplot(myplot,ggplot2::aes(x=dX,y=dY)) 118 | gg <- gg + ggplot2::geom_point(ggplot2::aes(colour= UMAP_1), size = 1.5) 119 | gg <- gg + ggplot2::geom_abline(slope=m) 120 | gg <- gg + ggplot2::coord_fixed() +ggplot2::ggtitle(inSituPlotTitle)+ggplot2::scale_color_viridis_c() 121 | print(gg) 122 | 123 | # Print the plots side by side 124 | # print(embeddings_plot|embeddings_histogram|gg) 125 | print("Generating plots...") 126 | print("If in situ spatial midline looks off, please adjust 'degs' parameter!") 127 | gridExtra::grid.arrange( 128 | gridExtra::arrangeGrob(embeddings_plot, embeddings_histogram, ncol = 1), 129 | gg, 130 | ncol = 2, 131 | widths = c(0.5, 0.5) 132 | ) 133 | # saving plot 134 | if (save_plot) { 135 | print('Saving plot...') 136 | dev.copy2eps(file = 'UMAP1EmbeddingsInSituMidline_plot.eps') 137 | print("Plot saved to working directory.")} 138 | 139 | #change default assay back to whatever it was before running function 140 | SeuratObject::DefaultAssay(object) <- current_default_assay 141 | 142 | # Return the merged data frame 143 | return(coordsEmbeddingsdf) 144 | } 145 | -------------------------------------------------------------------------------- /R/plotUMAP1_line_and_histogram.R: -------------------------------------------------------------------------------- 1 | #' Fetch UMAP_1 embeddings values for cells and their distance away from Spatial Midline 2 | #' @author Margarita Kapustina 3 | #' 4 | #' @description Fetches UMAP_1 embeddinga values for cells, and their distance away from the computed spatial midline. Supports one FOV at a time. 5 | #' 6 | #' @param object a Xenium object 7 | #' @param FOV FOV to extract coordinates and UMAP_1 embeddings values from 8 | #' @param degs User-defined angle, defined in degrees, that intersects centrepoint in midline computation 9 | #' @param binNumber Number of bins to use for histogram (bars). Suggested binNumber = total # cells in pooled data/binwidth 10 | #' @param binwidth Width of bins for gene expression averaging (lines). Suggested binwidth = 40 for 40micron bins 11 | #' 12 | #' @return Dataframe containing Cell IDs, coordinates (X,Y), UMAP_1 embedding values, 13 | #' and computed distance away from spatial midline of each cell, in specified FOV. 14 | #' @import ggplot2 15 | #' @import Seurat 16 | #' @import SeuratObject 17 | #' @import tibble 18 | #' 19 | #' @examples 20 | #' UMAP1_midline_data_fov <- getUMAP1_MidlineData(AD, FOV= c('fov'), degs = 65) 21 | #' UMAP1_midline_data_X1fov <- getUMAP1_MidlineData(AD, FOV= c('X1fov'), degs = 45) 22 | #' @export 23 | 24 | getUMAP1_MidlineData <- function(object, FOV, degs = 45, binNumber = 15, binwidth = 40) { 25 | 26 | # Fetch UMAP_1 embeddings for Xenium obj 27 | cell.embeddings = object@reductions$umap@cell.embeddings 28 | cell.embeddings.df = as.data.frame(cell.embeddings) 29 | names(cell.embeddings.df)[1] <- "UMAP_1" 30 | cell.embeddings.df <- tibble::rownames_to_column(cell.embeddings.df, "Cell_ID") 31 | 32 | #get current default assay to then set back later 33 | current_default_assay = SeuratObject::DefaultAssay(object) 34 | 35 | #set default FOV to fetch coordinates for FOV of choice 36 | SeuratObject::DefaultFOV(object) <- FOV 37 | 38 | # Get tissue coordinates 39 | df <- Seurat::GetTissueCoordinates(object) 40 | 41 | # Merge tissue coordinates with the dataframe containing 1D UMAP embeddings 42 | cells_to_get <- df[["cell"]] 43 | cell.embeddings.df_subset <- cell.embeddings.df[cell.embeddings.df$Cell_ID %in% cells_to_get, ] 44 | names(cell.embeddings.df_subset)[1] = 'cell' 45 | coordsEmbeddingsdf = merge(df, cell.embeddings.df_subset, by = 'cell') 46 | 47 | # Prepare data for plotting UMAP_1 embedding with nucleus midline 48 | names(coordsEmbeddingsdf)[names(coordsEmbeddingsdf) == 'x'] <- 'X' 49 | names(coordsEmbeddingsdf)[names(coordsEmbeddingsdf) == 'y'] <- 'Y' 50 | 51 | # Store .getCentre() Function 52 | .getCentre <- function(df, doMean = FALSE) { 53 | if(doMean) { 54 | dX <- mean(df$X) 55 | dY <- mean(df$Y) 56 | } else { 57 | dX <- median(df$X) 58 | dY <- median(df$Y) 59 | } 60 | 61 | dX <- df$X - dX 62 | dY <- df$Y - dY 63 | 64 | df <- cbind(df, dX, dY) 65 | 66 | return(df) 67 | } 68 | 69 | # Compute the centre of the coordinates 70 | centre = coordsEmbeddingsdf %>% .getCentre(doMean = TRUE) 71 | # Set slope of the line 72 | rads <- degs * pi / 180 73 | m <- tan(rads) 74 | # Run distance operation and make a new df "myplot" 75 | dLine <- (-m * centre$dX + centre$dY) / sqrt(1 + m^2) 76 | myplot <- cbind(centre, dLine) 77 | #plot out UMAP_1 embedding with nucleus midline 78 | gg <- ggplot2::ggplot(myplot,ggplot2::aes(x=dX,y=dY)) 79 | gg <- gg + ggplot2::geom_point(ggplot2::aes(colour= UMAP_1), size = 1.5) 80 | gg <- gg + ggplot2::geom_abline(slope=m) 81 | gg <- gg + ggplot2::coord_fixed() +ggplot2::ggtitle('UMAP_1 values across Spatial Midline')+ggplot2::scale_color_viridis_c() 82 | print(gg) 83 | 84 | # Print the plots side by side 85 | # print(embeddings_plot|embeddings_histogram|gg) 86 | print("Generating plots...") 87 | print("If in situ midline looks off, please adjust 'degs' parameter!") 88 | 89 | # ggplot2 Visualization 90 | #show UMAP_1 embeddings as a line graph, as a distance away from midline 91 | p1 = ggplot2::ggplot(myplot,ggplot2::aes(x = dLine)) + 92 | ggplot2::geom_histogram(ggplot2::aes(y = ggplot2::after_stat(count / 100)), bins = binNumber, boundary= 0, colour = 'green')+ 93 | ggplot2::coord_cartesian(xlim = c(-500, 500), ylim =c(-6,8))+ 94 | ggplot2::xlab('Distance away from Spatial Midline')+ 95 | ggplot2::ylab('Cells per bin/100')+ 96 | ggplot2::stat_summary_bin(ggplot2::aes(x = dLine, y= UMAP_1),fun = mean, binwidth = binwidth, geom = 'line', colour = 'blue') + 97 | #stat_bin(bins = 7, boundary = 0,geom = 'text', aes(label = ..count../100), vjust = 0, size = 3, color = 'black')+ 98 | ggplot2::ggtitle('UMAP_1 embeddings\nbins are mean number of cells\nper bin /100 in this FOV ONLY') 99 | print(p1|gg) 100 | 101 | # Calculate the number of cells in each bin 102 | bin_counts <- ggplot2::ggplot_build(p1)$data[[1]] 103 | cat("Number of cells in each bin (note: plotted are counts/100): 104 | Beginning on left-most bin...\n") 105 | for (i in seq_along(bin_counts$y)) { 106 | cat("Bin:", i, ", Count:", bin_counts$count[i], "\n") 107 | } 108 | 109 | # Annotate which FOV the data belongs to 110 | myplot$fov = FOV 111 | # make myplot df unique for each fov ########## 112 | # Creating a new object with a dynamic name 113 | dfTitle <- "UMAP1_midline_data" 114 | dfName <- paste(dfTitle, FOV, sep = "_") 115 | assign(dfName, myplot) 116 | 117 | print("If spatial midline looks off, please adjust 'degs' parameter!") 118 | message('Please save the dataframe with FOV identifier, such as: ', 119 | dfName) 120 | 121 | #change default assay back to whatever it was before running function 122 | SeuratObject::DefaultAssay(object) <- current_default_assay 123 | 124 | # Return the resulting df (for the FOV specified) 125 | return(get(dfName)) 126 | } 127 | 128 | #' Plots UMAP_1 embeddings values for cells and their distance away from the computed spatial midline (supports multiple FOVs) 129 | #' @author Margarita Kapustina 130 | #' 131 | #' @description Plot UMAP_1 embedding values for cells and their distance away from Spatial Midline across multiple FOVs. 132 | #' 133 | #' @param UMAP1MidlineData List of dataframes generated with getUMAP1_MidlineData() across multiple FOVs 134 | #' @param binNumber Number of bins to use for histogram (bars). Suggested binNumber = total # cells in pooled data/binwidth 135 | #' @param binwidth Width of bins for gene expression averaging (lines). Suggested binwidth = 40 for 40micron bins 136 | #' @param save_plot Option to save plot as .eps in working directory (TRUE, FALSE) 137 | #' 138 | #' @return Pooled dataframe containing Cell IDs, coordinates (X,Y), UMAP_1 embedding values, 139 | #' and computed distance away from spatial midline of each cell, across all FOVs in data 140 | #' 141 | #' @import ggplot2 142 | #' @import Seurat 143 | #' @import SeuratObject 144 | #' @import tibble 145 | #' 146 | #' @examples 147 | #' pooled_UMAP1_vsMidline = plotUMAP1_vsMidline(list(UMAP1_midline_data_fov, UMAP1_midline_data_X1fov)) 148 | #' @export 149 | 150 | plotUMAP1_vsMidline <- function(UMAP1MidlineData, 151 | binNumber = 7, binwidth =40, 152 | save_plot = FALSE) { 153 | # Use do.call to rbind the objects in the list 154 | pooled_UMAP_df <- do.call(rbind, UMAP1MidlineData) 155 | 156 | # ggplot2 Visualization 157 | #show UMAP_1 embeddings as a line graph, as a distance away from midline 158 | p1 = ggplot2::ggplot(pooled_UMAP_df,ggplot2::aes(x = dLine)) + 159 | ggplot2::geom_histogram(ggplot2::aes(y = ggplot2::after_stat(count / 100)), bins = binNumber, boundary= 0, colour = 'green')+ 160 | ggplot2::coord_cartesian(xlim = c(-500, 500), ylim =c(-6,8))+ 161 | ggplot2::xlab('Distance away from Spatial Midline')+ 162 | ggplot2::ylab('Cells per bin/100')+ 163 | ggplot2::stat_summary_bin(ggplot2::aes(x = dLine, y= UMAP_1),fun = mean, binwidth =binwidth, geom = 'line', colour = 'blue') + 164 | #stat_bin(bins = 7, boundary = 0,geom = 'text', aes(label = ..count../100), vjust = 0, size = 3, color = 'black')+ 165 | ggplot2::ggtitle('UMAP_1 embeddings across Spatial Midline') 166 | print(p1) 167 | 168 | # Calculate the number of cells in each bin 169 | bin_counts <- ggplot2::ggplot_build(p1)$data[[1]] 170 | cat("Number of cells in each bin (note: plotted are counts/100): 171 | Beginning on left-most bin...\n") 172 | for (i in seq_along(bin_counts$y)) { 173 | cat("Bin:", i, ", Count:", bin_counts$count[i], "\n") 174 | } 175 | 176 | # saving 177 | if (save_plot) { 178 | print('Saving plot...') 179 | dev.copy2eps(file = 'UMAP1_embeddings_vs_midline.eps') 180 | print("Plot saved to working directory.")} 181 | 182 | return(pooled_UMAP_df) 183 | } 184 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Xenium Spatial Analysis: R Toolkit 2 |

3 | 4 |

5 | 6 | 7 | This repository contains R tools and workflows for spatial analysis of cell-types using single-cell spatial transcriptomic Xenium data. To know more about Xenium technology, check out the [Xenium workflow](https://www.10xgenomics.com/support/software/xenium-onboard-analysis/latest). 8 | *** 9 | We will use the anterodorsal nucleus of the anterior thalamic nuclei (ATN) within two brain slices from [our Cell Reports paper](https://www.cell.com/cell-reports/fulltext/S2211-1247(24)00170-0?_returnURL=https%3A%2F%2Flinkinghub.elsevier.com%2Fretrieve%2Fpii%2FS2211124724001700%3Fshowall%3Dtrue) to demonstrate how to examine gene expression gradients. You can also check out the raw data and full Seurat processed object at our [GEO portal](https://www.ncbi.nlm.nih.gov/geo/query/acc.cgi?acc=GSE255953), or download just the anterodorsal nucleus of the ATN in two slices [here](https://osf.io/zae5v/?view_only=27b80485b12441478755fe61613743e3) in `.rds` file format to follow the tutorial. 10 | 11 | _July 9th 2024: New functions (analyzeLayer and object_FOV_to_coordinates) have been added along with tutorials!_ 12 | * `object_FOV_to_coordinates` allows you to extract cell ID, cluster ID, cell coordinates, etc. for cells from a specified FOV in your Xenium object. 13 | * `analyzeLayer` allows you to compute cell normalized distances along along a cortical layer, and away from a boundary that you define to examine superficial-deep differences in cell-type composition or spatial organization. 14 | 15 | ## Overview 16 | 1. [Toolkit Contents](#toolkit-contents) 17 | 2. [Citation](#citation) 18 | 3. [Installation](#installation) 19 | 4. [Tutorial](#tutorial) 20 | 5. [Function List](#function-list) 21 | 22 | # Toolkit Contents 23 | The following can be performed with this suite of tools: 24 | * create publication ready plots 25 | * merge and analyze data across multiple slices, via [UMAP dimensionality reduction](https://www.nature.com/articles/nbt.4314) and applying [cluster-based algorithms](https://www.tandfonline.com/doi/full/10.1080/15476286.2020.1728961) 26 | * highlight UMAP clusters of interest _in situ_ across selected FOV(s) 27 | * tally the number of cells in each Xenium Assay Seurat object 28 | * find marker genes for unique UMAP clusters 29 | * create boxplots for cluster-specific marker genes (using sequencing depth-corrected or raw counts) 30 | * calculate the midline of any group of cells _in situ_ 31 | * compute gene expression as a function of distance away from a computed midline 32 | * perform a gene expression gradient analysis (via computing 1-Dimensional UMAP embeddings values for cells) 33 | * compute UMAP_1 embeddings as a function of distance away from a computed midline 34 | * ... and more! 35 | 36 | # Citation 37 | If you find this Xenium Toolkit useful, please cite our paper: 38 | ```text 39 | Kapustina, M., Zhang, A.A., Tsai, J.Y.J., Bristow, B.N., Kraus, L., Sullivan, K.E., Erwin, S.R., Wang, L., Stach, T.R., Clements, J., et al. (2024). The cell-type-specific spatial organization of the anterior thalamic nuclei of the mouse brain. Cell Reports 43, 113842–113842. https://doi.org/10.1016/j.celrep.2024.113842. 40 | ``` 41 | 42 | # Installation 43 | > **Please ensure that your R version >= 4.3.2.** 44 | 45 | To install and load package: 46 | ```R 47 | devtools::install_github("MargoKapustina/XeniumSpatialAnalysis") 48 | library(XeniumSpatialAnalysis) 49 | ``` 50 | When updating to a newer version of the repo: 51 | ```R 52 | #remove old version 53 | remove.packages("XeniumSpatialAnalysis") 54 | 55 | #reinstall from here or from the cembrowskilab/XeniumSpatialAnalysis github 56 | devtools::install_github("MargoKapustina/XeniumSpatialAnalysis") 57 | ``` 58 | 59 | # Tutorial 60 | 61 | ## 1. Read in Data ## 62 | ### Read in raw Xenium data ### 63 | Read in raw Xenium data using Seurat's `LoadXenium()` function. Make sure to set unique FOV names for each slice for easier downstream processing. The code here also removes cells with zero counts, and saves data as .rds file in a specified directory. 64 | 65 | For each replicate assign a unique FOV for easier downstream processing: 66 | ```R 67 | #Author: Margarita Kapustina, UBC, 2023 68 | #read in xenium data, and remove cells with 0 counts, save rds files. 69 | 70 | #load packages 71 | library(Seurat) 72 | library(dplyr) 73 | 74 | #specify folder location (home) and where to save .rds object (save.location) 75 | home = "XeniumOutputFolder.path" 76 | save.location = "SaveLocation.path" 77 | 78 | #for each replicate assign a unique FOV for easier downstream processing: 79 | setwd(home) 80 | filename = 'output-XETG00098__0018569__rep1_atn_ant__20231129__015936' 81 | obj <- LoadXenium(filename, fov = "fov") 82 | # remove cells with 0 counts 83 | obj <- subset(obj, subset = nCount_Xenium > 0) 84 | setwd(save.location) 85 | saveRDS(obj, 'output-XETG00098__0018569__rep1_atn_ant__20231129__015936-obj.rds') 86 | rm(obj) 87 | ``` 88 | ## 2. Merge data ## 89 | ### Merge data across slices and animals ### 90 | Read in data from `.rds` files, add sample `metadata` to differentiate slices, and `merge` slices. 91 | ```R 92 | #load packages 93 | library(Seurat) 94 | library(dplyr) 95 | library(cowplot) 96 | library(ggplot2) 97 | 98 | #read in data from saved .rds files and merge all slices 99 | data.dir = "myobjpath1.rds" 100 | S1_ant = readRDS(data.dir) 101 | data.dir = "myobjpath2.rds" 102 | S1_int = readRDS(data.dir) 103 | data.dir = "myobjpath3.rds" 104 | S1_post = readRDS(data.dir) 105 | 106 | #add sample metadata 107 | S1_ant@meta.data$sample = 'S1_ant' 108 | S1_int@meta.data$sample = 'S1_int' 109 | S1_post@meta.data$sample = 'S1_post' 110 | 111 | #merge slices 112 | merge = merge(S1_ant, S1_int) 113 | merge = merge(merge, S1_post) 114 | ``` 115 | ## 3. Analyze data ## 116 | ### 3a. Analyze merged data ### 117 | Analyze data across multiple slices, via [UMAP dimensionality reduction](https://www.nature.com/articles/nbt.4314) and applying [cluster-based algorithms](https://www.tandfonline.com/doi/full/10.1080/15476286.2020.1728961). Continue until you have desired **UMAP embeddings**. 118 | For more info see [Seurat's vigentte](https://satijalab.org/seurat/articles/seurat5_spatial_vignette_2.html). 119 | 120 | ```R 121 | #normalize merged data 122 | merge <- SCTransform(merge, assay = "Xenium") 123 | #remove oligos and endothelial cells - your genes of choice here 124 | xenexc = merge %>% subset(Slc17a6 > 0 & Opalin ==0 & Sox10 ==0 & Sox17 ==0) 125 | #visualize subset of cells in specified FOV 126 | ImageDimPlot(xenexc, fov = 'X1fov') 127 | 128 | #run analysis 129 | xenexc <- RunPCA(xenexc, npcs = 30, features = rownames(xenexc)) 130 | #select your number of dims based on elbow plot 131 | #fyi elbow plot: a ranking of principle components based on the 132 | #percentage of variance explained by each one (see: https://satijalab.org/seurat/articles/pbmc3k_tutorial.html) 133 | ElbowPlot(xenexc, ndims = 30, reduction = "pca") 134 | xenexc <- RunUMAP(xenexc, dims = 1:30) 135 | xenexc <- FindNeighbors(xenexc, reduction = "pca", dims = 1:30) 136 | xenexc <- FindClusters(xenexc, resolution = 0.03) 137 | 138 | #visualize UMAP 139 | DimPlot(xenexc, raster = FALSE, cols = 'glasbey') + DarkTheme() +coord_fixed() 140 | 141 | #find the number of cells in your object 142 | dim(xenexc) #75058 - excitatory neuronal cells 143 | ``` 144 | To highlight a cluster in your Xenium object, use `HighlightCluster()` and specify: 145 | * `obj`: Xenium object 146 | * `cluster_id`: Identity of cluster to highlight 147 | * `FOV`: FOV(s) of choice to vizualise cells within 148 | > Following are __optional__ to specify: 149 | > * `size`: Size of the highlighted cells in the plot 150 | > * `alpha_value`: Alpha (transparency) value of the highlighted cells 151 | > * `color_palette`: Color palette for the plot 152 | > * `save_plot`: Option to save plot as .pdf in working directory (TRUE, FALSE) 153 | ```R 154 | highlightCluster(obj = xenexc, cluster_id = c('2', '6'), size = 2, FOV = c('X1fov', 'fov')) 155 | ``` 156 |

157 | 158 |

159 | 160 | ### 3b. Subset clusters of choice for downstream analysis ### 161 | Subset clusters from a Xenium object with `subset()`. 162 | ```R 163 | #subset clusters 164 | xen_atn = xenexc %>% subset(idents = c("2", "6")) 165 | ``` 166 | 167 | 168 | #### Next, to highlight your subset object within the original object, use `HighlightCells()` and specify: ##### 169 | * `highlight_obj`: the object you want to highlight (ie subset cells) 170 | * `within_obj`: the object you want to highlight your object within (ie cells plotted but not highlighted; original object) 171 | * `FOV`: FOV of choice to vizualise cells within 172 | > __Optional__ to specify: 173 | > * `size`: Size of the highlighted cells in the plot 174 | > * `alpha_value`: Alpha (transparency) value of the highlighted cells 175 | > * `color_palette`: Color palette for the plot 176 | > * `save_plot`: Option to save plot as .pdf in working directory (TRUE, FALSE) 177 | ```R 178 | #highlight subset of cells within original object (or any smaller object within a larger object) 179 | highlightCells(highlight_obj = xen_atn, within_obj = xenexc, size = 1) 180 | ``` 181 |

182 | 184 | 185 | #### Repeat these steps until you are satisfied with your resulting subset of cells. #### 186 | 187 | ## 4. Prep your spatial gradient analysis ## 188 | #### 4a. Create marker gene boxplots for each cluster #### 189 | Create boxplots for gene of interest across all clusters, using `XeniumBoxPlot()` or `XeniumBoxPlotRaw()` 190 | ```R 191 | #create vector of genes 192 | my_genes= c('C1ql2','Foxp1', 'Gng13') 193 | 194 | #generate boxplots using sequencing depth corrected counts (these are SCT$counts) 195 | BoxPlots = XeniumBoxPlot(object = xen_atn, genes = my_genes) 196 | #or using raw counts (these are Xenium$counts) 197 | BoxPlotsRaw = XeniumBoxPlotRaw(object = xen_atn, genes = my_genes) 198 | ``` 199 |

200 | 202 | 203 | ### 4b. Visualize clusters chosen for analysis in space ### 204 | Before performing your spatial gradient analysis or compute the spatial midline, visualize your cells in space with `highlightCells()`. 205 | ```R 206 | #subset cells in specific clusters (see 3a) 207 | AD = xen_atn_subregions %>% subset(idents = c('5', '6')) 208 | #visualize the subset 209 | ImageDimPlot(AD, cols = "polychrome", size = 2, fov = 'X1fov') 210 | #highlight cells that we subset 211 | highlightCells(highlight_obj = AD, within_obj = xen_atn_subregions) 212 | ``` 213 |

214 | 216 | 217 | > [!IMPORTANT] 218 | > **Prior to running any 1D UMAP plots (i.e. plotUMAP1_inSituMidline) UMAP_1 embeddings must be computed.** 219 | >

220 | > User can compute UMAP_1 embeddings using Seurat 221 | >
222 | > #subset your cluster(s) of interest
223 | > AD = xen_atn_subregions %>% subset(idents = c('5', '6'))
224 | >
225 | >  #(optional) visualize and check cells included in subset 
226 | > Seurat::ImageDimPlot(AD, cols = "polychrome", size = 2, fov = 'X1fov') 
227 | >
228 | >  #load required package
229 | > library(Seurat)
230 | >  #compute 1D UMAP embeddings
231 | > AD<- Seurat::RunPCA(AD, npcs = 30, features = rownames(AD))
232 | >  #choose number of dims for UMAP accordingly
233 | > Seurat::ElbowPlot(AD, ndims = 30, reduction = "pca")
234 | > AD <- Seurat::RunUMAP(AD, dims = 1:15, n.components = 1)
235 | >
236 | > For more more info on Seurat functions see (https://satijalab.org/seurat/articles/install_v5) 
237 | >
238 | 239 | ## 5. Perform your spatial gradient analysis ## 240 | ### 5a. Perform spatial gene expression analysis using UMAP_1 embeddings ### 241 | Here, we will: 242 | i. Create a 1-dimensional UMAP 243 | ii. Plot the corresponding histogram of UMAP_1 embedding values & the UMAP_1 embedding values *in situ* within specified FOV 244 | iii. Compute the midline intersecting cells within specified FOV 245 | 246 | 247 | First, run `plotUMAP1_inSituMidline()`, making sure to check how your spatial midline looks. If it looks off - please adjsut `degs` parameter. When using the function specify: 248 | * `object` a Xenium object 249 | * `FOV` FOV to extract coordinates from 250 | * `degs` User-defined angle, defined in degrees, that intersects centrepoint in midline computation 251 | > * `EmbeddingsPlotTitle` title for UMAP_1 Embeddings plot 252 | > * `HistogramPlotTitle` title for UMAP_1 Embeddings Histogram plot 253 | > * `inSituPlotTitle `title for UMAP_1 Embeddings in situ plot 254 | > * `save_plot` Option to save plot as .eps in working directory (TRUE, FALSE) 255 | ```R 256 | #Plot 1D UMAP, corresponding histogram of UMAP_1 embedding values, UMAP_1 embedding values in situ (within specified FOV), and compute the midline intersecting cells (within specified FOV). 257 | #note: please compute UMAP_1 embeddings beforehand. 258 | AD_df_midline = plotUMAP1_inSituMidline(object = AD, FOV = 'X1fov', degs = 45, save_plot = TRUE) 259 | ``` 260 |

261 | 263 | 264 | 265 | You can also create the above plots without computing the spatial midline using `plotUMAP1_inSitu()` When using the function specify: 266 | * `object` a Xenium object 267 | * `FOV` FOV to plot UMAP_1 embedding values in 268 | > * `EmbeddingsPlotTitle` title for UMAP_1 Embeddings plot 269 | > * `HistogramPlotTitle` title for UMAP_1 Embeddings Histogram plot 270 | > * `inSituPlotTitle `title for UMAP_1 Embeddings in situ plot 271 | > * `save_plot` Option to save plot as .eps in working directory (TRUE, FALSE) 272 | ```R 273 | #Plot 1D UMAP, corresponding histogram of UMAP_1 embedding values, UMAP_1 embedding values in situ (within specified FOV) 274 | #note: please compute UMAP_1 embeddings beforehand. 275 | AD_df = plotUMAP1_inSitu(object = AD, FOV = 'X1fov', save_plot = TRUE) 276 | ``` 277 |

278 | 280 | 281 | 282 | ### 5b. Perform spatial gene expression analysis using individual genes ### 283 | First generate Gene Expression vs Midline data for individual FOVs using `getExpressionvsMidline()`. Make sure to visually assess whether your midline intersects your cells appropriatley. When using this function, please specify: 284 | * `object` a Xenium object 285 | * `genes` Character vector of gene names to fetch expression data for 286 | * `FOV` FOV to extract coordinates from 287 | * `degs` User-defined angle, defined in degrees, that intersects centrepoint in midline computation 288 | ```R 289 | #generate gene expression vs spatial midline data for individual FOVs, for any gene(s) in your assay 290 | geneExpression_X1fov <- getExpressionvsMidline(AD, FOV = "X1fov", genes = c("Epha1", "Cntnap4", 'Pcp4', 'Mdga1'), degs = 45) 291 | geneExpression_fov <- getExpressionvsMidline(AD, FOV = "fov", genes = c("Epha1", "Cntnap4", 'Pcp4', 'Mdga1'), degs = 60) 292 | ``` 293 | 294 |

295 | 296 | 297 |

298 | 299 | *** 300 | Then, use `plotGeneExpressionVsMidline()` to create a pooled dataframe containing Cell IDs, coordinates (X,Y), gene expression counts for specififed gene(s), and computed distance away from spatial midline of each cell, across all FOVs in data. Running `getExpressionvsMidline()` will also plot gene expression data for cells and their distance away from Spatial Midline across _multiple_ FOVs. When using this function, please specify: 301 | * `geneExpressionData` List of dataframes generated with getExpressionvsMidline() across multiple FOVs 302 | * `genes` Character vector of gene names to fetch expression data for 303 | > __Optional__ to specify: 304 | > * `binNumber` Number of bins to use for histogram (bars). Suggested binNumber = total # cells in pooled data/binwidth (default: 7) 305 | > * `binwidth` Width of bins for gene expression averaging (lines) Suggested binwidth = 40 for 40micron bins (default: 40) 306 | > * `save_plot` Option to save plot as .eps in working directory (TRUE, FALSE) 307 | > * `xlim` Define x-axis limits as vector (default: -500 to 500) 308 | ```R 309 | #pool data and plot gene expression vs spatial midline data for multiple genes, across multiple FOVs 310 | #lines are gene expression values (averaged gene counts per binned cells) & histogram is number of cells in each bin 311 | pooled_GeneExpression = plotGeneExpressionVsMidline(geneExpressionData = list(geneExpression_fov, geneExpression_X1fov), genes = c("Epha1", "Cntnap4", 'Pcp4', 'Mdga1')) 312 | ``` 313 |

314 | 316 | 317 | 318 |

319 | You will also get a readout of the colour legend per genes and the number of cells per bin when running this function 320 |
Colour Scheme (Gene : colour):
321 | Epha1 : red 
322 | Cntnap4 : green 
323 | Pcp4 : pink 
324 | Mdga1 : black 
325 | [1] "Generating plot..."
326 | Number of cells in each bin (note: plotted are counts/100):
327 | Beginning on left-most bin...
328 | Bin: 1 , Count: 11 
329 | Bin: 2 , Count: 28 
330 | Bin: 3 , Count: 66 
331 | Bin: 4 , Count: 78 
332 | Bin: 5 , Count: 29 
333 | Bin: 6 , Count: 21 
334 | Bin: 7 , Count: 1 
335 |
336 | 337 | > You can also run the function on just a single genExpression dataframe: 338 | ```R 339 | #pool data and plot gene expression vs spatial midline data for multiple genes, for one FOV 340 | #lines are gene expression values (averaged gene counts per binned cells) & histogram is number of cells in each bin 341 | pooled_GeneExpression = plotGeneExpressionVsMidline(geneExpressionData = list(geneExpression_fov), genes = c("Epha1", "Cntnap4", 'Pcp4', 'Mdga1')) 342 | ``` 343 | #### _note on selecting `genes`_: #### 344 | > It's recommended that you specifiy the same `genes` for `plotGeneExpressionVsMidline()` as `getExpressionvsMidline()`. If you would like to get gene expression data for more genes, run `getExpressionvsMidline()` with your new desired genes, and then run `plotGeneExpressionVsMidline()` after. If you keep the dataframe name the same, running the function will overwrite the previous gene expression dataframe stored in your environment. 345 | *** 346 | In a similar fashion to plotting gene expression data as a function of distance away from spatial midline, you can also plot the UMAP_1 embeddings as a function of distance away from midline. To do this, use `getUMAP1_MidlineData()` then `plotUMAP1_vsMidline()`. 347 | When using `getUMAP1_MidlineData()` please specify: 348 | * `object` a Xenium object 349 | * `FOV` FOV to extract coordinates and UMAP_1 embeddings values from 350 | > __Optional__ to specify: 351 | > * `binNumber` Number of bins to use for histogram (bars). Suggested binNumber = total # cells in pooled data/binwidth (default: 7) 352 | > * `binwidth` Width of bins for gene expression averaging (lines) Suggested binwidth = 40 for 40micron bins (default: 40) 353 | > * `save_plot` Option to save plot as .eps in working directory (TRUE, FALSE) 354 | 355 | ```R 356 | #generate UMAP_1 embedding values vs spatial midline data for individual FOVs, specifying degrees to define spatial midline for each individual FOV 357 | #will also plot coloured coordinates according to UMAP_1 embeddings and histogram of UMAP_1 embeddings values per binned distance for single FOV specified 358 | ##note: please compute UMAP_1 embeddings beforehand 359 | UMAP1_midline_data_fov <- getUMAP1_MidlineData(AD, FOV= c('fov'), degs = 65) 360 | UMAP1_midline_data_X1fov <- getUMAP1_MidlineData(AD, FOV= c('X1fov'), degs = 45) 361 | ``` 362 |

363 | 365 | 366 | When using `plotUMAP1_vsMidline()` please specify: 367 | * `UMAP1MidlineData` List of dataframes generated with getUMAP1_MidlineData() across multiple FOVs 368 | > __Optional__ to specify: 369 | > * `binNumber` Number of bins to use for histogram (bars). Suggested binNumber = total # cells in pooled data/binwidth (default: 7) 370 | > * `binwidth` Width of bins for gene expression averaging (lines) Suggested binwidth = 40 for 40micron bins (default: 40) 371 | > * `save_plot` Option to save plot as .eps in working directory (TRUE, FALSE) 372 | 373 | ```R 374 | #pool data and plot histogram of UMAP_1 embeddings values per binned distance across multiple FOVs 375 | pooled_UMAP1_vsMidline = plotUMAP1_vsMidline(list(UMAP1_midline_data_fov, UMAP1_midline_data_X1fov)) 376 | ``` 377 |

378 | 380 | 381 | ### Beta Functions: 382 | To examine whether your cells exhibit superficial-deep transcriptomic differences, the following examples will guide you through extracting cell coordinates with relevant information and computing distance along a cortical layer and cell distances away from a boundary that you define. 383 | 384 | First, use `object_FOV_to_coordinates()` to create a dataframe with cell ID coordinates and cluster IDs from a Xenium Seurat object. This function will also allow you to define a boundary for each FOV, that you specifiy if you are interested in assessing whether cell cluster IDs differ above, and below, a regional boundary that you define. The function will return a dataframe with cell ID, coordinates, cluster ID, and a plot with UMAP_1 embedding values and the boundary that you define. Please make sure that you have already run dimensionality reduction and clustering on your Xenium object before running this function. 385 | 386 | When using `object_FOV_to_coordinates()` please specify: 387 | * `object` Xenium object to extract cell coordinates from 388 | * `thisFOV` Name of the FOV to extract coordinates from 389 | * `threshold_y` Boundary that you define to compute cells that are above and below this 390 | * `angle_adjust` Option to adjust the angle of your cell coordinates (TRUE, FALSE) 391 | * `theta_deg` Specify the degrees you wish to rotate your slice by 392 | * `flip_x_coordinates` Option to flip your slice in the horizontal plane (TRUE, FALSE) 393 | 394 | ```R 395 | distanceData_X11fov = object_FOV_to_coordinates(object, thisFOV = 'X11fov', threshold_y = 1911, 396 | angle_adjust = TRUE, 397 | theta_deg = 20, 398 | flip_x_coordinates = TRUE) 399 | ``` 400 | 401 |

402 | 404 | 405 | Once you have run object_FOV_to_coordinates() on all slices, merge your FOV-specific dataframes using rbind(), and compute the normalized distance away from the boundary for each of the slices. 406 | 407 | ```R 408 | #merge all distancedistanceData 409 | distance_data_all = rbind(distanceData_X2fov, distanceData_X3fov) 410 | distance_data_all= rbind(distance_data_all, distanceData_X4fov) 411 | distance_data_all= rbind(distance_data_all, distanceData_X11fov) 412 | 413 | #add normalized distance 414 | distance_data_all$normalized_distance = (distance_data_all$y - distance_data_all$threshold) 415 | ``` 416 | Now, use `analyzeLayer()` to compute the cell normalized distances along the cortical layer and away from a boundary that you define. To define your boundary, click sequential points in your plot screen, and hit `ESC` when complete. The boundary defined will be showed in the plot screen. 417 | When using `analyzeLayer()` please specify: 418 | * `thisFOV` Name of our FOV to extract coordinates from 419 | 420 | __Make sure that you have your merged distanceData stored as distance_data_all__ 421 | ```R 422 | #make sure that you have merged distanceData stored as distance_data_all 423 | #if you only have one FOV, rename the dataframe to distance_data_all 424 | 425 | #save your working directory to where plot output wil be saved 426 | setwd("/myPlots") 427 | cellDataX11fov <- analyzeLayer("X11fov") 428 | dev.copy2eps(file = 'X11fovAnalysis.eps') 429 | ``` 430 |

431 | 433 | 434 | _Upon running this function, several plots will be saved. These plots show:_ 435 | > * normalized distance along the cortical layer 436 | > * normalized distance to boundary of the cortical layer 437 | > * histogram of distances along the cortical layer 438 | > * histogram of distances to the boundary of the cortical layer 439 | 440 |

441 | 442 | 443 | 444 | 445 |

446 | 447 | Next, you can merge FOV-specific cell data into a merged dataframe to compare cell cluster ID and normalized distance along the neocoritcal layer, or away from the defined boundary 448 | ```R 449 | #merge all cellData 450 | allCellData = rbind(cellDataX2fov, cellDataX3fov) 451 | allCellData= rbind(allCellData, cellDataX4fov) 452 | allCellData= rbind(allCellData, cellDataX11fov) 453 | ``` 454 | 455 | # Function List 456 | `HighlightCluster` 457 | * highlight cluster(s) of choice in a Xenium object 458 | {_cluster of interest labelled "this cluster" & all other clusters in the object labelled "all else"_} 459 | 460 | `HighlightCells` 461 | * highlight subset cells from a Xenium object of choice -- useful when subsetting cells for downstream analysis 462 | {_cells from subset object labelled "these cells" & all other cells labelled "all else"_} 463 | 464 | `XeniumBoxPlot` 465 | * plot boxplots of given genes per cluster using sequencing-depth corrected counts 466 | 467 | `XeniumBoxPlotRaw` 468 | * plot boxplots of given genes per cluster using raw Xenium counts 469 | 470 | `getExpressionvsMidline` 471 | * generates gene expression data away from the computed spatial midline. Supports one FOV at a time 472 | 473 | `plotGeneExpressionvsMidline` 474 | * plots gene expression data for cells and their distance away from Spatial Midline across multiple FOVs 475 | 476 | `getUMAP1_MidlineData` 477 | * fetches UMAP_1 embedding values for cells, and their distance away from the computed spatial midline. Supports one FOV at a time 478 | 479 | `plotUMAP1_vsMidline` 480 | * plots UMAP_1 embedding values for cells and their distance away from Spatial Midline across multiple FOVs 481 | 482 | `plotUMAP1inSitu` 483 | * plots 1-dimensional UMAP, the corresponding histogram of UMAP_1 embedding values, and the UMAP_1 embedding values in situ within specified FOV 484 | 485 | `plotUMAP1inSitu_Midline` 486 | * plots gene expression data for cells and their distance away from Spatial Midline across multiple FOVs 487 | 488 | #### Beta Functions 489 | `object_FOV_to_coordinates` 490 | * plots gene expression data for cells and their distance away from Spatial Midline across multiple FOVs 491 | 492 | `analyzeLayer` 493 | * plots gene expression data for cells and their distance away from Spatial Midline across multiple FOVs 494 | 495 | 496 | > Interested in recreating the tutorial from our processed Xenium seurat object? 497 | ```R 498 | #read in seurat obj from .rds file (access via GEO portal) 499 | xen_atn_analysis = readRDS('xen_atn_analysis.rds') 500 | #increase resolution "high/fine resolution" 501 | xen_atn_subregions = xen_atn_analysis 502 | xen_atn_subregions <- FindClusters(xen_atn_subregions, resolution = 0.7) 503 | AD = xen_atn_subregions %>% subset(idents = c('5', '6')) 504 | 505 | ## or read in AD data directly (fovs included: fov, X1fov) access via OSF portal 506 | AD = readRDS('exampleAD.rds') 507 | ``` 508 | 509 | 510 | 511 | [back to top](#top) 512 | -------------------------------------------------------------------------------- /man/XeniumBoxPlot.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/boxplots.R 3 | \name{XeniumBoxPlot} 4 | \alias{XeniumBoxPlot} 5 | \title{Xenium Gene Box Plots} 6 | \usage{ 7 | XeniumBoxPlot(object, genes, ncol = 4, save_plot = FALSE) 8 | } 9 | \arguments{ 10 | \item{object}{Xenium object} 11 | 12 | \item{genes}{Character vector of gene names to plot} 13 | 14 | \item{ncol}{Specify number of columns in plot} 15 | 16 | \item{save_plot}{Option to save plot as .eps in working directory (TRUE, FALSE)} 17 | } 18 | \value{ 19 | A grid of boxplots showing gene expression across all clusters 20 | } 21 | \description{ 22 | Create a boxplot denoting expression of gene(s) of interest across all clusters. SCT assay counts (sequencing depth corrected counts) are used. 23 | } 24 | \examples{ 25 | #create vector ie my_genes= c('C1ql2','Slc17a7', 'Gng13') 26 | BoxPlots = XeniumBoxPlot(object = atn, genes = my_genes) 27 | } 28 | \author{ 29 | Margarita Kapustina 30 | } 31 | -------------------------------------------------------------------------------- /man/XeniumBoxPlotRaw.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/boxplotsRaw.R 3 | \name{XeniumBoxPlotRaw} 4 | \alias{XeniumBoxPlotRaw} 5 | \title{Xenium Gene Box Plots - Raw Counts} 6 | \usage{ 7 | XeniumBoxPlotRaw(genes, ncol = 4, object, save_plot = FALSE) 8 | } 9 | \arguments{ 10 | \item{genes}{Character vector of gene names to plot} 11 | 12 | \item{ncol}{Specify number of columns in plot} 13 | 14 | \item{object}{Xenium object} 15 | 16 | \item{save_plot}{Option to save plot as .eps in working directory (TRUE, FALSE)} 17 | } 18 | \value{ 19 | A grid of boxplots showing raw gene expression across all clusters 20 | } 21 | \description{ 22 | Create a boxplot denoting expression of gene(s) of interest across all clusters. Xenium assay counts (raw counts) are used. 23 | } 24 | \examples{ 25 | #create vector ie my_genes= c('C1ql2','Slc17a7', 'Gng13') 26 | BoxPlots = XeniumBoxPlotRaw(object = atn, genes = my_genes) 27 | } 28 | \author{ 29 | Margarita Kapustina 30 | } 31 | -------------------------------------------------------------------------------- /man/analyzeLayer.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/analyzeLayer.R 3 | \name{analyzeLayer} 4 | \alias{analyzeLayer} 5 | \title{Calculate the normalized distance along a cortical layer and away from a boundary for cell coordinates} 6 | \usage{ 7 | analyzeLayer(thisFOV) 8 | } 9 | \arguments{ 10 | \item{thisFOV}{Name of your FOV to define your boundary within} 11 | } 12 | \value{ 13 | A dataframe with cell ID, coordinates, cluster ID, normalized distance along a cortical layer, and distance away from your defined boundary. 14 | } 15 | \description{ 16 | Compute distance along a cortical layer, and the distance away from a defined boundary, using distance data computed with object_FOV_to_coordinates function. Note: please save your data computed object_FOV_to_coordinates named as 'distance_data_all'. If you have multiple FOVs run through the object_FOV_to_coordinates function, merge them and rename the object. If you have only a single FOV, simply rename the object. 17 | } 18 | \examples{ 19 | #before running the function, please save merged FOV-specific data computed with the object_FOV_to_coordinates function, as distance_data_all 20 | #example: distance_data_all = rbind(distanceData_X2fov, distanceData_X3fov) 21 | #Please noe that distance_data_all needs to be within your environment to run analyzeLayer 22 | cellDataX3fov <- analyzeLayer("X3fov") 23 | } 24 | \author{ 25 | Margarita Kapustina 26 | } 27 | -------------------------------------------------------------------------------- /man/getExpressionvsMidline.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/Get_Plot_geneExpressionvsMidline.R 3 | \name{getExpressionvsMidline} 4 | \alias{getExpressionvsMidline} 5 | \title{Generate Gene Expression values for cells and their distance away from Spatial Midline} 6 | \usage{ 7 | getExpressionvsMidline(object, FOV = "fov", genes, degs) 8 | } 9 | \arguments{ 10 | \item{object}{a Xenium object} 11 | 12 | \item{FOV}{FOV to extract coordinates from} 13 | 14 | \item{genes}{Character vector of gene names to fetch expression data for} 15 | 16 | \item{degs}{User-defined angle, defined in degrees, that intersects centrepoint in midline computation} 17 | } 18 | \value{ 19 | Dataframe containing Cell IDs, coordinates (X,Y), gene expression counts for specififed gene(s), 20 | and computed distance away from spatial midline of each cell, in specified FOV. 21 | } 22 | \description{ 23 | Generates gene expression data away from the computed spatial midline. Supports one FOV at a time. 24 | } 25 | \examples{ 26 | geneExpression_X1fov <- getExpressionvsMidline(AD, FOV = "X1fov", genes = c("Epha1", "Cntnap4", 'Pcp4', 'Slc17a7'), degs = 45) 27 | geneExpression_fov <- getExpressionvsMidline(AD, FOV = "fov", genes = c("Epha1", "Cntnap4", 'Pcp4', 'Slc17a7'), degs = 50) 28 | } 29 | \author{ 30 | Margarita Kapustina 31 | } 32 | -------------------------------------------------------------------------------- /man/getUMAP1_MidlineData.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/plotUMAP1_line_and_histogram.R 3 | \name{getUMAP1_MidlineData} 4 | \alias{getUMAP1_MidlineData} 5 | \title{Fetch UMAP_1 embeddings values for cells and their distance away from Spatial Midline} 6 | \usage{ 7 | getUMAP1_MidlineData(object, FOV, degs = 45, binNumber = 15, binwidth = 40) 8 | } 9 | \arguments{ 10 | \item{object}{a Xenium object} 11 | 12 | \item{FOV}{FOV to extract coordinates and UMAP_1 embeddings values from} 13 | 14 | \item{degs}{User-defined angle, defined in degrees, that intersects centrepoint in midline computation} 15 | 16 | \item{binNumber}{Number of bins to use for histogram (bars). Suggested binNumber = total # cells in pooled data/binwidth} 17 | 18 | \item{binwidth}{Width of bins for gene expression averaging (lines). Suggested binwidth = 40 for 40micron bins} 19 | } 20 | \value{ 21 | Dataframe containing Cell IDs, coordinates (X,Y), UMAP_1 embedding values, 22 | and computed distance away from spatial midline of each cell, in specified FOV. 23 | } 24 | \description{ 25 | Fetches UMAP_1 embeddinga values for cells, and their distance away from the computed spatial midline. Supports one FOV at a time. 26 | } 27 | \examples{ 28 | UMAP1_midline_data_fov <- getUMAP1_MidlineData(AD, FOV= c('fov'), degs = 65) 29 | UMAP1_midline_data_X1fov <- getUMAP1_MidlineData(AD, FOV= c('X1fov'), degs = 45) 30 | } 31 | \author{ 32 | Margarita Kapustina 33 | } 34 | -------------------------------------------------------------------------------- /man/highlightCells.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/highlightCells_highlightCluster.R 3 | \name{highlightCells} 4 | \alias{highlightCells} 5 | \title{Highlight Cells} 6 | \usage{ 7 | highlightCells( 8 | highlight_obj, 9 | within_obj, 10 | size = 3, 11 | alpha_value = 0.5, 12 | FOV = c("fov", "X1fov"), 13 | color_palette = "glasbey", 14 | save_plot = FALSE 15 | ) 16 | } 17 | \arguments{ 18 | \item{highlight_obj}{Xenium object that you want to highlight cells from, labelled: "these cells"} 19 | 20 | \item{within_obj}{Xenium object that you want show highlighted cells within, labelled: "all else" (ie cells plotted but not highlighted)} 21 | 22 | \item{size}{Size of the highlighted cells in the plot} 23 | 24 | \item{alpha_value}{Alpha (transparency) value of the highlighted cells} 25 | 26 | \item{FOV}{Character vector specifying the fields of view (FOVs) to use for plotting} 27 | 28 | \item{color_palette}{Color palette for the plot} 29 | 30 | \item{save_plot}{Option to save plot as .pdf in working directory (TRUE, FALSE)} 31 | } 32 | \value{ 33 | An ImageDimPlot with highlighted cells. 34 | } 35 | \description{ 36 | Highlight cells from a Xenium object of interest, within another Xenium object, within specified FOV(s). 37 | Cells from object of interest labelled as "these cells", other cells labelled as "all else". Suggested to use when subsetting Xenium objects. 38 | } 39 | \examples{ 40 | highlightCells(highlight_obj = atn, within_obj = ExcitatoryNeurons, FOV = c('X1fov')) 41 | 42 | } 43 | \author{ 44 | Margarita Kapustina 45 | } 46 | -------------------------------------------------------------------------------- /man/highlightCluster.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/highlightCells_highlightCluster.R 3 | \name{highlightCluster} 4 | \alias{highlightCluster} 5 | \title{Highlight Cluster} 6 | \usage{ 7 | highlightCluster( 8 | object, 9 | cluster_id, 10 | size = 3, 11 | alpha_value = 0.5, 12 | FOV = c("fov", "X1fov"), 13 | color_palette = "glasbey", 14 | save_plot = FALSE 15 | ) 16 | } 17 | \arguments{ 18 | \item{object}{a Xenium object} 19 | 20 | \item{cluster_id}{Identity of cluster to highlight} 21 | 22 | \item{size}{Size of the highlighted cells in the plot} 23 | 24 | \item{alpha_value}{Alpha (transparency) value of the highlighted cells} 25 | 26 | \item{FOV}{Character vector specifying the fields of view (FOVs) to use for plotting} 27 | 28 | \item{color_palette}{Color palette for the plot} 29 | 30 | \item{save_plot}{Option to save plot as .pdf in working directory (TRUE, FALSE)} 31 | } 32 | \value{ 33 | An ImageDimPlot with highlighted cells. 34 | } 35 | \description{ 36 | Highlight cluster(s) of choice in a Xenium object, within specified FOV(s). Cluster of interest labelled "this cluster" 37 | and all other clusters labelled "all else". Supported for clusters within the same object. 38 | } 39 | \examples{ 40 | highlightCluster(object = obj, cluster_id = 1, FOV = c('X1fov', 'X2fov')) 41 | 42 | } 43 | \author{ 44 | Margarita Kapustina 45 | } 46 | -------------------------------------------------------------------------------- /man/object_FOV_to_coordinates.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/ObjectToCoords.R 3 | \name{object_FOV_to_coordinates} 4 | \alias{object_FOV_to_coordinates} 5 | \title{Get cell coordinates from a Xenium object} 6 | \usage{ 7 | object_FOV_to_coordinates( 8 | object = object, 9 | thisFOV = "X9fov", 10 | threshold_y = 2000, 11 | angle_adjust = FALSE, 12 | theta_deg = 0, 13 | flip_x_coordinates = FALSE 14 | ) 15 | } 16 | \arguments{ 17 | \item{object}{Xenium object} 18 | 19 | \item{thisFOV}{Name of your FOV to extract coordinates from} 20 | 21 | \item{threshold_y}{Boundary that you define to compute cells that are above and below this boundary} 22 | 23 | \item{angle_adjust}{Option to adjust the angle of your cell coordinates (TRUE, FALSE)} 24 | 25 | \item{flip_x_coordinates}{Option to flip your slice in the horizontal plane (TRUE, FALSE)} 26 | 27 | \item{thetda_deg}{Specify the degrees you wish to rotate your slice by} 28 | } 29 | \value{ 30 | A dataframe with cell ID, coordinates, cluster ID, and a plot with UMAP_1 embedding values and the boundary that you define 31 | } 32 | \description{ 33 | Create a dataframe with cell ID coordinates and cluster IDs from a Xenium Seurat object. 34 | } 35 | \examples{ 36 | distanceData_X2fov = object_FOV_to_coordinates(object = a, thisFOV = 'X2fov', threshold_y = 2694, angle_adjust = FALSE, theta_deg = 0, flip_x_coordinates = FALSE) 37 | } 38 | \author{ 39 | Margarita Kapustina 40 | } 41 | -------------------------------------------------------------------------------- /man/plotGeneExpressionVsMidline.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/Get_Plot_geneExpressionvsMidline.R 3 | \name{plotGeneExpressionVsMidline} 4 | \alias{plotGeneExpressionVsMidline} 5 | \title{Plots gene expression data away from the computed spatial midline (supports multiple FOVs)} 6 | \usage{ 7 | plotGeneExpressionVsMidline( 8 | geneExpressionData, 9 | genes, 10 | binNumber = 7, 11 | binwidth = 40, 12 | save_plot = FALSE, 13 | xlim = c(-500, 500) 14 | ) 15 | } 16 | \arguments{ 17 | \item{geneExpressionData}{List of dataframes generated with getExpressionvsMidline() across multiple FOVs} 18 | 19 | \item{genes}{Character vector of gene names to their plot expression data} 20 | 21 | \item{binNumber}{Number of bins to use for histogram (bars). Suggested binNumber = total # cells in pooled data/binwidth} 22 | 23 | \item{binwidth}{Width of bins for gene expression averaging (lines) Suggested binwidth = 40 for 40micron bins} 24 | 25 | \item{save_plot}{Option to save plot as .eps in working directory (TRUE, FALSE)} 26 | 27 | \item{xlim}{Define x-axis limits as vector.} 28 | } 29 | \value{ 30 | Pooled dataframe containing Cell IDs, coordinates (X,Y), gene expression counts for specififed gene(s), 31 | and computed distance away from spatial midline of each cell, across all FOVs in data 32 | } 33 | \description{ 34 | Plot gene expression data for cells and their distance away from Spatial Midline across multiple FOVs. 35 | } 36 | \examples{ 37 | pooled_GeneExpression = plotGeneExpressionVsMidline(geneExpressionData = list(geneExpression_fov, geneExpression_fX1ov), genes = c("Epha1", "Cntnap4", 'Pcp4', 'Slc17a7')) 38 | } 39 | \author{ 40 | Margarita Kapustina 41 | } 42 | -------------------------------------------------------------------------------- /man/plotUMAP1_inSitu.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/plotUMAP1_inSItu.R 3 | \name{plotUMAP1_inSitu} 4 | \alias{plotUMAP1_inSitu} 5 | \title{Xenium 1-D UMAP Embeddings Plotted in Situ} 6 | \usage{ 7 | plotUMAP1_inSitu( 8 | object, 9 | FOV = "X1fov", 10 | EmbeddingsPlotTitle = "UMAP_1 embedding for all\\n cells in subset", 11 | HistogramPlotTitle = "Distribution of UMAP_1 embedding\\n values all cells in subset", 12 | inSituPlotTitle = "UMAP_1 embedding in situ\\n for representative FOV", 13 | save_plot = FALSE 14 | ) 15 | } 16 | \arguments{ 17 | \item{object}{a Xenium object} 18 | 19 | \item{FOV}{FOV to plot UMAP_1 embedding values in} 20 | 21 | \item{EmbeddingsPlotTitle}{title for UMAP_1 Embeddings plot} 22 | 23 | \item{HistogramPlotTitle}{title for UMAP_1 Embeddings Histogram plot} 24 | 25 | \item{inSituPlotTitle}{title for UMAP_1 Embeddings in situ plot} 26 | 27 | \item{save_plot}{Option to save plot as .eps in working directory (TRUE, FALSE)} 28 | } 29 | \value{ 30 | Dataframe containing Cell IDs, coordinates (X,Y) and computed UMAP_1 embeddings values for cells in specified FOV. 31 | #Plots 1D UMAP, corresponding histogram of UMAP_1 embeddings values, and UMAP_1 embeddings values plotted _in situ_. 32 | } 33 | \description{ 34 | Plots: 1-dimensional UMAP, the corresponding histogram of UMAP_1 embedding values, and 35 | the UMAP_1 embedding values in situ within specified FOV. 36 | } 37 | \examples{ 38 | AD_df = plotUMAP1_inSitu(object = AD, FOV = 'fov') 39 | } 40 | \author{ 41 | Margarita Kapustina 42 | } 43 | -------------------------------------------------------------------------------- /man/plotUMAP1_inSituMidline.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/plotUMAP1_inSituMidline.R 3 | \name{plotUMAP1_inSituMidline} 4 | \alias{plotUMAP1_inSituMidline} 5 | \title{Xenium 1-D UMAP Embeddings Plotted in Situ} 6 | \usage{ 7 | plotUMAP1_inSituMidline( 8 | object, 9 | FOV = "X1fov", 10 | degs = 45, 11 | save_plot = FALSE, 12 | EmbeddingsPlotTitle = "UMAP_1 embedding for all\\n cells in subset", 13 | HistogramPlotTitle = "Distribution of UMAP_1 embedding\\n values all cells in subset", 14 | inSituPlotTitle = "UMAP_1 embedding in situ\\n for representative FOV" 15 | ) 16 | } 17 | \arguments{ 18 | \item{object}{a Xenium object} 19 | 20 | \item{FOV}{FOV to plot UMAP_1 embedding values in} 21 | 22 | \item{degs}{User-defined angle, defined in degrees, that intersects centrepoint in midline computation} 23 | 24 | \item{save_plot}{Option to save plot as .eps in working directory (TRUE, FALSE)} 25 | 26 | \item{EmbeddingsPlotTitle}{title for UMAP_1 Embeddings plot} 27 | 28 | \item{HistogramPlotTitle}{title for UMAP_1 Embeddings Histogram plot} 29 | 30 | \item{inSituPlotTitle}{title for UMAP_1 Embeddings in situ plot} 31 | } 32 | \value{ 33 | Dataframe containing Cell IDs, coordinates (X,Y) and computed UMAP_1 embeddings values for cells in specified FOV. 34 | #Plots 1D UMAP, corresponding histogram of UMAP_1 embeddings values, and UMAP_1 embeddings values plotted in situ. 35 | } 36 | \description{ 37 | Plots: 1-dimensional UMAP, the corresponding histogram of UMAP_1 embedding values, 38 | the UMAP_1 embedding values in situ within specified FOV, and computes the midline intersecting cells within specified FOV. 39 | note: please compute UMAP_1 embeddings beforehand. 40 | } 41 | \examples{ 42 | AD_df = plotUMAP1_inSituMidline(object = AD, FOV = 'fov', degs = 65) 43 | } 44 | \author{ 45 | Margarita Kapustina 46 | } 47 | -------------------------------------------------------------------------------- /man/plotUMAP1_vsMidline.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/plotUMAP1_line_and_histogram.R 3 | \name{plotUMAP1_vsMidline} 4 | \alias{plotUMAP1_vsMidline} 5 | \title{Plots UMAP_1 embeddings values for cells and their distance away from the computed spatial midline (supports multiple FOVs)} 6 | \usage{ 7 | plotUMAP1_vsMidline( 8 | UMAP1MidlineData, 9 | binNumber = 7, 10 | binwidth = 40, 11 | save_plot = FALSE 12 | ) 13 | } 14 | \arguments{ 15 | \item{UMAP1MidlineData}{List of dataframes generated with getUMAP1_MidlineData() across multiple FOVs} 16 | 17 | \item{binNumber}{Number of bins to use for histogram (bars). Suggested binNumber = total # cells in pooled data/binwidth} 18 | 19 | \item{binwidth}{Width of bins for gene expression averaging (lines). Suggested binwidth = 40 for 40micron bins} 20 | 21 | \item{save_plot}{Option to save plot as .eps in working directory (TRUE, FALSE)} 22 | } 23 | \value{ 24 | Pooled dataframe containing Cell IDs, coordinates (X,Y), UMAP_1 embedding values, 25 | and computed distance away from spatial midline of each cell, across all FOVs in data 26 | } 27 | \description{ 28 | Plot UMAP_1 embedding values for cells and their distance away from Spatial Midline across multiple FOVs. 29 | } 30 | \examples{ 31 | pooled_UMAP1_vsMidline = plotUMAP1_vsMidline(list(UMAP1_midline_data_fov, UMAP1_midline_data_X1fov)) 32 | } 33 | \author{ 34 | Margarita Kapustina 35 | } 36 | --------------------------------------------------------------------------------