I have some doubts about distinguishing sender spots from receiver spots?
I was also wondering how does one decide on a good distance (experimentally sensible) for visium data. I have used a distance of 200 (based on the distance argument in stlearn cci ). I was wondering if this is appropriate?
Also I have doubts about deciding which spots are receiver spots or sender spots.
I used the following code (in which I have 3 versions for deciding sender spots and receiver spots).
In the following code I have 3 versions (version 1 and 3 are similar) ,in those 2 versions I take the spot which is significant for a particular Ligand receptor pair and filter the neighbourhood spots (if it expresses receptor to some degree e.g. >1 or >2 etc) and further filter the Ligand based on expression of Ligand in the significant spot (i.e. if the ligand expression is not > 1 count I don't consider it). Although I have seen significant spots which don't show expression of that particular Ligand or receptor (and I was wondering why is that so)?
In the second version I just use the significant spots (assuming that the significant spots are the only ones which have spatially significant ligand and receptor expression (further filtering based on ligand and receptor expression in that spot being > 1 count).
So I was wondering what do you guys think about it? If this is the correct approach? If not what else would you recommend?
Thank you!
sigspots_senders_to_receiverspot_list <- function(sigspots_file, neighbourhood_spots_file, seurat_obj) {
# Load the neighbourhood spots data
neighbourhood_spots_df <- read.table(neighbourhood_spots_file, sep = " ", header = TRUE)
# Create list to store sender spots and their neighborhoods
sender_spot_neighborhood_list <- list()
for (i in 1:nrow(neighbourhood_spots_df)) {
sender_spot <- neighbourhood_spots_df$X[i]
sender_spot_neighborhood_list[[sender_spot]] <- str_split(neighbourhood_spots_df$neighbour_bcs[i], ',')[[1]]
}
# Load significant spots data
sigspots_df <- read.csv(sigspots_file, sep = ",", header = TRUE)
sigspots_df_list <- split(sigspots_df, sigspots_df$LR_interaction)
# Initialize list to store results
LR_sender_receiver_spot_list <- list()
# Iterate over each ligand-receptor interaction
for (LR in names(sigspots_df_list)) {
myligand <- str_split(LR, '_')[[1]][1]
myrec <- str_split(LR, '_')[[1]][2]
# Identify barcodes with expression for ligand and receptor
# For version 1 receptor_barcodes <- names(which(seurat_obj@assays$Spatial@counts[myrec, ] > 0)) #to further filter neighborhood barcodes to only include higher expression barcodes for receptors
#version 3 recepter expression > 2 and ligand expression > 2
receptor_barcodes <- names(which(seurat_obj@assays$Spatial@counts[myrec, ] > 2)) #to further filter neighborhood barcodes to only include higher expression barcodes for receptors
ligand_barcodes <- names(which(seurat_obj@assays$Spatial@counts[myligand, ] > 2))
# Filter significant sender spots based on ligand expression
mysig_sender_spots <- intersect(sigspots_df_list[[LR]]$Significant_barcodes, ligand_barcodes)
# Version 3 is only use spots for a particular LR if they are in Significant_barcodes consider those which are
# Find receiver barcodes in the neighborhood of sender spots where receptor is expressed
#version 3 recepter expression > 2 and ligand expression > 2
# Version 2 is only use spots for a particular LR if they are in Significant_barcodes consider those which are
# Find receiver barcodes in the neighborhood of sender spots where receptor is expressed
#Version 1 and version 3
receptor_barcodes_in_neighbourhood <- lapply(sender_spot_neighborhood_list[mysig_sender_spots], function(x) {
intersect(receptor_barcodes, x)
})
# Remove empty elements
# Version 2 use receptors spots only if they are in mysig_sender_spots
# receptor_barcodes_in_neighbourhood <- lapply(sender_spot_neighborhood_list[mysig_sender_spots], function(x) {
# abc = intersect(receptor_barcodes, x)
# return(intersect(abc,mysig_sender_spots))
# })
receptor_barcodes_in_neighbourhood <- Filter(function(x) length(x) > 0, receptor_barcodes_in_neighbourhood)
# Skip if no valid sender spots or all neighborhoods are empty
if (length(mysig_sender_spots) == 0 || length(receptor_barcodes_in_neighbourhood) == 0) {
next
}
# Create data frames for each sender-receiver pair
Sender_spot_receiver_spot_df_list <- lapply(names(receptor_barcodes_in_neighbourhood), function(x) {
mydf <- data.frame(receiver_spot = receptor_barcodes_in_neighbourhood[[x]])
mydf$sender_spot <- x
mydf$LR <- LR
return(mydf[c('sender_spot', 'receiver_spot', 'LR')])
})
# Combine data frames into one for each LR interaction
Sender_spot_receiver_spot_df <- do.call(rbind, Sender_spot_receiver_spot_df_list)
LR_sender_receiver_spot_list[[LR]] <- Sender_spot_receiver_spot_df
}
# Combine all LR interaction data frames into a single data frame
LR_sender_receiver_spot_df <- do.call(rbind, LR_sender_receiver_spot_list)
# Add sample metadata to LR_sender_receiver_spot_df
mysample_seurat_meta <- seurat_obj@meta.data[c('integrated_snn_res.0.9')]
mysample_seurat_meta$barcode <- rownames(mysample_seurat_meta)
mysample_seurat_meta$integrated_snn_res.0.9 = droplevels(mysample_seurat_meta$integrated_snn_res.0.9)
LR_sender_receiver_spot_df$Sample <- unique(seurat_obj$Sample)
LR_sender_receiver_spot_df$Patient <- unique(seurat_obj$Patient)
# Join metadata and rename columns correctly
LR_sender_receiver_spot_df2 <- LR_sender_receiver_spot_df %>%
left_join(mysample_seurat_meta, by = c("sender_spot" = "barcode")) %>%
rename(sender_cluster = integrated_snn_res.0.9)
LR_sender_receiver_spot_df3 <- LR_sender_receiver_spot_df2 %>%
left_join(mysample_seurat_meta, by = c("receiver_spot" = "barcode")) %>%
rename(receiver_cluster = integrated_snn_res.0.9)
# Return the final data frame
return(LR_sender_receiver_spot_df3)
}
Hi,
I have some doubts about distinguishing sender spots from receiver spots? I was also wondering how does one decide on a good distance (experimentally sensible) for visium data. I have used a distance of 200 (based on the distance argument in stlearn cci ). I was wondering if this is appropriate?
Also I have doubts about deciding which spots are receiver spots or sender spots.
I used the following code (in which I have 3 versions for deciding sender spots and receiver spots).
In the following code I have 3 versions (version 1 and 3 are similar) ,in those 2 versions I take the spot which is significant for a particular Ligand receptor pair and filter the neighbourhood spots (if it expresses receptor to some degree e.g. >1 or >2 etc) and further filter the Ligand based on expression of Ligand in the significant spot (i.e. if the ligand expression is not > 1 count I don't consider it). Although I have seen significant spots which don't show expression of that particular Ligand or receptor (and I was wondering why is that so)?
In the second version I just use the significant spots (assuming that the significant spots are the only ones which have spatially significant ligand and receptor expression (further filtering based on ligand and receptor expression in that spot being > 1 count).
So I was wondering what do you guys think about it? If this is the correct approach? If not what else would you recommend?
Thank you!