专栏名称: 生信菜鸟团
生信菜鸟团荣誉归来,让所有想分析生物信息学数据的小伙伴找到归属,你值得拥有!
目录
相关文章推荐
生信人  ·  第二轮通知丨2025年CACA肿瘤标志物学术 ... ·  3 天前  
生物学霸  ·  万蕊雪获全国三八红旗手称号 ·  2 天前  
BioArt  ·  Science | ... ·  3 天前  
生信宝典  ·  Nature Communications ... ·  3 天前  
51好读  ›  专栏  ›  生信菜鸟团

R tips:使用最近邻算法进行空间浸润带的计算

生信菜鸟团  · 公众号  · 生物  · 2025-02-15 21:00

正文

本文使用最近邻算法进行浸润带的计算。

空间组学中,有的时候需要对免疫浸润带进行特定距离的划分,形成一层一层的浸润区域。

以10X的官方xenium示例数据为例https://www.10xgenomics.com/datasets/ffpe-human-breast-with-pre-designed-panel-1-standard。

圈选ROI并计算浸润边界

下载的数据使用Xenium explorer打开,然后找到需要进行计算浸润带的位置,并根据方向将相应的全部选中。

如下图所示,假设中间的位置是需要进行浸润带计算的位置,而需要计算浸润带的方向是向下,则在Xenium explorer中选择套索工具仔细的圈画浸润边界,并将浸润带计算方向上的所有细胞选中。

然后在Xenium explorer中将图示的ROI区域的边界坐标下载下来。

由于Xenium explorer无法圈画单条线,至少也是一个闭合区域,因此需要先计算好浸润边界的位置:

  1. library(tidyverse)

  2. library(sp)

  3. # 读取ROI的边界坐标及细胞的质心坐标

  4. tumor_boundary <- read_csv("data/coordinates.csv", skip = 2)

  5. cells_position <- arrow::read_parquet("Xenium_V1_FFPE_Human_Breast_IDC/cells.parquet")

  6. # 然后根据ROI边界坐标,将细胞分配到ROI中

  7. cell_idx <-

  8. sp::point.in.polygon(

  9. cells_position$x_centroid, # point

  10. cells_position$y_centroid, # point

  11. tumor_boundary$X , # polygon

  12. tumor_boundary$Y

  13. ) %>%

  14. as.logical # only 0 is FALSE, all others is TRUE

  15. # 于是tumor_area_1即是ROI中的细胞,图示中的所有细胞

  16. # tumor_area_2是剩余细胞,也就是图示中的上方未被框选中的细胞

  17. tumor_area_1 <- cells_position[cell_idx, ] %>% mutate(x = x_centroid, y = y_centroid )

  18. tumor_area_2 <- cells_position[!cell_idx, ] %>% mutate(x = x_centroid, y = y_centroid )

获得了浸润边界的两组细胞之后,就可以进行浸润边界的计算:

  1. # 根据tumor_area_1和tumor_area_2找到绘制的tumor boundary

  2. # 寻找1和2中的最近邻点,以20um(大概一个细胞,根据效果调整)为限度,

  3. # 如果没有nn点则不是边界点。

  4. # 找到1和2中的边界点,取均值即是最终的边界点

  5. # dat: tumor_area_1

  6. # query: tumor_area_2

  7. nn_left_right <-

  8. RANN::nn2(

  9. tumor_area_1[2:3],

  10. tumor_area_2[2:3],

  11. k = 10,

  12. searchtype = 'radius',

  13. radius = 20 # 若边界未能找到,则调整radius的值

  14. )

  15. non_boundary_idx <- apply(nn_left_right$nn.idx == 0, 1, all)

  16. if(sum(non_boundary_idx) == nrow(tumor_area_2)){

  17. print("radius is small, no boundary point.")

  18. }

  19. boundary_idx_1 <- nn_left_right$nn.idx[! non_boundary_idx, 1]

  20. boundary_1 <- tumor_area_1[boundary_idx_1, ]

  21. boundary_idx_2 <- ! non_boundary_idx

  22. boundary_2 <- tumor_area_2[boundary_idx_2, ]

  23. # comuted boudary coords

  24. boundary_coords_df <-

  25. data.frame(

  26. x = (boundary_1$x_centroid + boundary_2$x_centroid) / 2,

  27. y = (boundary_1$y_centroid + boundary_2$y_centroid) / 2

  28. )

绘图展示,计算的圈画的浸润边界位置:

  1. # 只展示浸润边界附近区域的细胞

  2. boundary_df_for_plot <-

  3. boundary_coords_df %>%

  4. .[chull(.), ] %>%

  5. map(range) %>%

  6. map(~ .x + c(-500, 500))

  7. # 定义用于筛选细胞的函数

  8. filter_points <- function(dat, boundary){

  9. x_idx <- dat$x_centroid > boundary$x[1] & dat$x_centroid < boundary$x[2]

  10. y_idx <- dat$y_centroid > boundary$y[1] & dat$y_centroid < boundary$y[2]

  11. dat [x_idx & y_idx, ]

  12. }

  13. # 绘图

  14. ggplot() +

  15. geom_point(

  16. # left area

  17. data = tumor_area_1 %>% dplyr::slice_sample(prop = 0.1) %>% filter_points(boundary_df_for_plot), # 仅绘制10%的点,加快绘制速度

  18. aes(x = x_centroid, y = -y_centroid),

  19. color = "gray"

  20. ) +

  21. geom_point(

  22. # right area

  23. data = tumor_area_2 %>% dplyr::slice_sample(prop = 0.1) %>% filter_points(boundary_df_for_plot), # 仅绘制10%的点,加快绘制速度

  24. aes(x = x_centroid, y = -y_centroid),

  25. color = "gray50"

  26. ) +

  27. geom_point(

  28. # comute left_boundary

  29. data = boundary_1,

  30. aes(x = x, y = -y),

  31. color = "red",

  32. size = 0.1

  33. ) +

  34. geom_point(

  35. # compute right_boundary

  36. data = boundary_2,

  37. aes(x = x, y = -y),

  38. color = "blue",

  39. size = 0.1

  40. ) +

  41. geom_point(

  42. # the final tumor boundary

  43. data = boundary_coords_df,

  44. aes(x = x, y = -y),

  45. color = "green",

  46. size = 0.1

  47. ) +

  48. theme_void()

如下图所示,计算的浸润边界是绿色点,用于计算浸润边界的上下边界配对点是红蓝色点。

使用最近邻算法往下寻找浸润区域

假设需要以250um为单位,分别找到250um 500um及750um的浸润区域,则可如下操作:

先定义一个最近邻的工具函数:

  1. # reduceFindNN find all (k) nn points in a certain radius,

  2. # k would be increased when k is not enough for a certain radius

  3. reduceFindNN <- function(dat, k = 10, query = NULL, searchtype = "radius", radius = 100, mpp = 1, max_k = 2000){

  4. if(is.null(query)){

  5. query <- dat

  6. }

  7. stopifnot(all(c('x', 'y') %in% colnames(dat)))

  8. stopifnot(all(c('x', 'y') %in% colnames(query)))

  9. dat <- dat[c('x', 'y')]

  10. query <- query[c('x', 'y')]

  11. nn_dat <- RANN::nn2(

  12. dat,

  13. query = query,

  14. k = k,

  15. radius = radius / mpp # radius, um -> pixel

  16. )

  17. k_enough <- sum(apply(nn_dat$nn.idx == 0, 1, any))/nrow(nn_dat$nn.idx)

  18. cat(str_glue("k is {k} and is enough for {k_enough * 100}% cells.\n\n"))

  19. while(k < max_k && k_enough < 0.99){

  20. if(k < 700){

  21. k <- k + 100

  22. }else if(k < 2000){

  23. k <- k + 200

  24. }else if(k < 5000){

  25. k <- k + 500

  26. }else{

  27. k <- k + 1000

  28. }

  29. nn_dat <- RANN ::nn2(

  30. dat,

  31. query = query,

  32. searchtype = searchtype,

  33. k = k,

  34. radius = radius / mpp # radius, um -> pixel

  35. )

  36. k_enough <- sum(apply(nn_dat$nn.idx == 0, 1, any))/nrow(nn_dat$nn.idx)

  37. cat(str_glue("k is {k} and is enough for {k_enough * 100}% cells.\n\n"))

  38. }

  39. return(nn_dat)

  40. }

浸润带的计算:

  1. radius_ls <- c(250, 500, 750) %>% set_names(., .)

  2. coords_tumor_infiltration_ls <-

  3. radius_ls %>%

  4. map(function(radius){

  5. dat_nn <-

  6. reduceFindNN(

  7. dat = tumor_area_1, # %>% rbind(tumor_boundary_coords) %>% distinct(),

  8. query = boundary_coords_df, # tumor_boundary_coords,

  9. radius = radius,

  10. mpp = 1,

  11. k = 100,

  12. max_k = nrow(tumor_area_1) # 2000

  13. )

  14. # all nn points is infiltration points

  15. points_idx <- dat_nn$nn.idx %>% c() %>% .[. != 0] %>% unique

  16. # infiltration area

  17. coords <- tumor_area_1[points_idx, ]

  18. coords







请到「今天看啥」查看全文