在R语言中,我们需要对字符串、向量和数据框等数据类型进行替换操作,有时候是因为需要更换别名,有时候是因为数据存在错误需要修正,有时候则是因为需要删除某些信息。本文将介绍常用的替换函数gsub的常用用法,但gsub也存在某些局限性,一般只能进行一次指定情况的操作。例如在单细胞数据分析的信息注释过程中,我们常常需要把无监督聚类得到的clusters注释成细胞类型,如果每一个clusters都写一行替换的代码就会显得相当冗余,因此可以封装成一个函数进行类似的处理就会简单一些。因此,本文后半部分将介绍批量替换写成函数的方法。
R语言中,最常用的替换函数是gsub,其用法也比较容易理解,一般只需传入三个参数,gsub(匹配内容,替换内容,操作对象):
string='strings'
gsub('s','S',string)
[1] "StringS"
string='strings'
gsub('s','',string)
[1] "tring"
string='strings'
gsub('^s','S',string)
[1] "Strings"
gsub('s$','S',string)
string='strings'
gsub('^s|i','X',string)
[1] "XtrXngs"
string='strings'
gsub('s..','X',string)
[1] "Xings"
string='strings'
gsub('i.*','X',string)
[1] "strX"
#不用方括号
string='^strings'
gsub('^','X',string)
[1] "X^strings"
#使用方括号
gsub("[.^]",'X',string)
[1] "Xstrings"
#不用方括号
> gsub(".^",'X',string)
[1] "^strings"
string='0strings1'
gsub('[0-9]','X',string)
[1] "XstringsX"
string='0strings1'
gsub('[a-z]','X',string)
[1] "0XXXXXXX1"
以Celltype列为例,以下all为Seurat对象,也可以是数据框。
all$Celltype[which(all$seurat_clusters == "24")]<-"XCL+ NK"
all$Celltype[grep("24",all$seurat_clusters)]<-"XCL+ NK"
all$Celltype[grepl("24",all$seurat_clusters)]<-"XCL+ NK"
以上方法都比较好理解,但是如果clusters比较多,写起来就很冗余,有时候clusters多达几十种,写起来不美观,可以写个函数封装一下。
其实单细胞数据注释也可以看成是一种对数据框的替换操作,因此可以提取Seurat对象的meta.data信息运行以下函数。
参数简介:
anno_cell <- function(meta,anno.list=NULL,ref.col='seurat_clusters',anno.col='Celltype'){
colist <- unique(colnames(meta))
if (length(grep(anno.col,colist))==0) {
meta[,anno.col] <- 'unknown'
}
nlen <- length(anno.list)
for (i in 1:nlen) {
name <- names(anno.list)[i]
clust <- anno.list[[name]]
c1 <- which(meta[,ref.col] %in% clust)
if (length(c1)!=0) {
meta[,anno.col][c1] = name
}
}
return(meta)
}
使用示例(obj是Seurat对象):
olic <- c(1,13,11,3,23,18)
micc <- c(6,22)
vlmc <- c(37,40)
nfol <- c(38)
opcc <- c(2)
unknownc <- c(39)
inc <- c(31,25,16,29)
exc <- c(14,12,7,32,34,28,27,24,4,0,35,8,10,30,33,21,36,19)
ast <- c(17,20,41,9,5,15,26)
obj@meta.data <- anno_cell(obj@meta.data,anno.list=list("OLI"=olic,
"MIC"=micc,
"ENDO"=vlmc,
"NFOL"=nfol,
"OPC"=opcc,
"IN"=inc,
"EX"=exc,
"AST"=ast)
以上就完成注释了,可以通过Dimplot函数检查注释得是否正确
有时候,注释结果不是根据无监督聚类得到的,而是需要从其它方法,或者亚群聚类得到信息来注释,简单来说就是需要把从数据框df1中信息转移到数据框df2中,但df1和df2的维度不相等,因此需要进行匹配注释。当然,Seurat自带AddMetaData函数,但由于维度问题,可能会报错。
参数简介如下:
map_anno <- function(ref,que,ref.col='Celltype',que.col='Celltype'){
colist <- unique(colnames(que))
if (length(grep(que.col,colist))==0) {
que[,que.col] <- 'unknown'
}
c1 <- which(rownames(ref) %in% rownames(que))
if (length(c1)!=dim(ref)[1]) {
print('Note: some cells in ref are not in que!')
ref <- ref[c1,]
}
nlen <- length(unique(ref[,ref.col]))
for (i in 1:nlen) {
name <- unique(ref[,ref.col])[i]
c1 <- which(ref[,ref.col] %in% name)
cellist <- rownames(ref)[c1]
que[cellist,que.col] <- name
}
return(que)
}
使用示例:
df1 <- read.table('cell_info.xls',sep="\t",header=T,stringsAsFactor=F)
obj@meta.data <- map_anno(ref=df1,que=obj@meta.data)
最近拿到师兄给的注释表格是把seurat_clusters从0群到n群的注释表格,如下图:

seq_anno <- function(meta,ref.col='seurat_clusters', anno.col='Celltype',anno.list=NULL) {
colist <- unique(colnames(meta))
meta$ref.col <- meta[,ref.col]
if (length(grep(anno.col,colist))==0) {
meta[,anno.col] <- 'unknown'
}
nmax <- max(meta$ref.col)
for (i in 0:nmax){
meta[,anno.col][grep(i,meta$ref.col)] <- anno.list[i+1]
}
return(meta)
}
meta为数据框,cell.anno为向量
> head(cell.anno)
[1] "CD16+Mono" "CD14+Mono" "CD16+Mono" "CD16+Mono" "CD1C+DC" "CD1C+DC"
> meta <- seq_anno(meta,anno.list=cell.anno,ref.col='seurat_clusters', anno.col='Celltype')
可以简写为
df2 <- seq_anno(df1,anno.list=cell.anno)
基因ID与基因名之间的替换是分析过程中容易遇到的问题,简单来说可以写个循环,但是基因有上万个时就比较麻烦了,不过已经有人写了现成的函数。
目标:df2是基因ID与基因名的映射关系,需要把df1的gene列的基因ID替换成基因名。
df1数据结构:
p_val avg_logFC pct.1 pct.2 p_val_adj cluster
AMEX60DD035333 1.276051e-76 0.2897204 0.811 0.836 3.139595e-72 0
AMEX60DD000806 4.985819e-61 0.5910303 0.739 0.894 1.226711e-56 0
AMEX60DD039553 1.624536e-44 0.2744919 0.175 0.469 3.997009e-40 0
AMEX60DD025106 1.054587e-41 0.2832646 0.145 0.396 2.594705e-37 0
AMEX60DD008073 2.048906e-40 0.2503263 0.136 0.375 5.041127e-36 0
AMEX60DD002836 4.554993e-40 0.2639632 0.168 0.430 1.120710e-35 0
gene FC
AMEX60DD035333 AMEX60DD035333 1.336054
AMEX60DD000806 AMEX60DD000806 1.805848
AMEX60DD039553 AMEX60DD039553 1.315862
AMEX60DD025106 AMEX60DD025106 1.327456
AMEX60DD008073 AMEX60DD008073 1.284444
AMEX60DD002836 AMEX60DD002836 1.302080
df2数据结构:
Axolotl_ID hs_gene
1 AMEX60DD000002 ZNF569
2 AMEX60DD000006 ZNF141
3 AMEX60DD000016 FZD10
4 AMEX60DD000033 GLT1D1
library(stringi)
df3 <- stri_replace_all_regex(df1$gene,df2$Axolotl_ID,df2$hs_gene,vectorize=F)
library(mgsub)
df4 <- mgsub(df1$gene,df2$Axolotl_ID,df2$hs_gene)
两个函数用法一样,输入三个参数:待替换向量,映射关系的原始ID,映射关系的替换内容。实测stri_replace_all_regex比mgsub快,推荐使用stri_replace_all_regex。
map_index <- function(df,index=NULL,content=NULL,mapcol=NULL,newcol='newcol'){
df1 <- data.frame(content)
rownames(df1) <- index
df2 <- df1[df[,mapcol],]
df[,'newcol'] <- df2
return(df)
}
df3 <- map_index(df2,index=celt,content=major,mapcol='Celltype',newcol='newcol')
使用到的数据格式如下:
> head(df2)
orig.ident nCount_Spatial nFeature_Spatial percent.mt nCount_SCT
BIN.214 Spatial 3096 1030 0.38759690 11238
BIN.371 Spatial 5609 1716 0.44571225 11438
BIN.374 Spatial 11045 2608 0.09959258 11533
BIN.375 Spatial 9026 2246 0.33237314 11646
BIN.378 Spatial 9520 2412 0.23109244 11541
BIN.379 Spatial 12353 3047 0.09714239 12151
nFeature_SCT SCT_snn_res.0.5 seurat_clusters BIN.100 bin100.y bin100.x
BIN.214 1814 0 0 BIN.214 2 52
BIN.371 1853 4 4 BIN.371 3 47
BIN.374 2608 4 4 BIN.374 3 50
BIN.375 2248 4 4 BIN.375 3 51
BIN.378 2412 4 4 BIN.378 3 54
BIN.379 3047 2 2 BIN.379 3 55
refined_pred ref.col major sub Celltype Major
BIN.214 10 10 mDC mDC mDC DC
BIN.371 5 5 LC LC_2 LC LC
BIN.374 5 5 LC LC_2 LC LC
BIN.375 5 5 LC LC_2 LC LC
BIN.378 5 5 LC LC_2 LC LC
BIN.379 5 5 LC LC_2 LC LC
> celt
[1] "CP" "DMC" "DVR" "EG"
[5] "LC" "lDC" "MC" "mDC"
[9] "PT" "Septum_l" "Septum_m" "Striatum"
[13] "Striatum_CCK" "Striatum_TAC4" "VLMC"
> major
[1] "CP" "HIP" "DVR" "VZ" "LC" "DC" "HIP" "DC"
[9] "PT" "Septum" "Septum" "STR" "STR" "STR" "VLMC"
注意index和content是对应的映射关系向量,顺序不能乱,且index不能有重复的元素。
替换操作不只有gsub函数这一种方法,我写的2个函数就没有使用到gsub,当然也可以写成gsub的脚本,但已经写好了就懒得写了。
在我的应用程序中,我需要能够找到所有数字子字符串,然后扫描每个子字符串,找到第一个匹配范围(例如5到15之间)的子字符串,并将该实例替换为另一个字符串“X”。我的测试字符串s="1foo100bar10gee1"我的初始模式是1个或多个数字的任何字符串,例如,re=Regexp.new(/\d+/)matches=s.scan(re)给出["1","100","10","1"]如果我想用“X”替换第N个匹配项,并且只替换第N个匹配项,我该怎么做?例如,如果我想替换第三个匹配项“10”(匹配项[2]),我不能只说s[matches[2]]="X"因为它做了两次替换“1fooX0barXg
我想在一个没有Sass引擎的类中使用Sass颜色函数。我已经在项目中使用了sassgem,所以我认为搭载会像以下一样简单:classRectangleincludeSass::Script::FunctionsdefcolorSass::Script::Color.new([0x82,0x39,0x06])enddefrender#hamlengineexecutedwithcontextofself#sothatwithintemlateicouldcall#%stop{offset:'0%',stop:{color:lighten(color)}}endend更新:参见上面的#re
我正在尝试用ruby中的gsub函数替换字符串中的某些单词,但有时效果很好,在某些情况下会出现此错误?这种格式有什么问题吗NoMethodError(undefinedmethod`gsub!'fornil:NilClass):模型.rbclassTest"replacethisID1",WAY=>"replacethisID2andID3",DELTA=>"replacethisID4"}end另一个模型.rbclassCheck 最佳答案 啊,我找到了!gsub!是一个非常奇怪的方法。首先,它替换了字符串,所以它实际上修改了
我有一些代码在几个不同的位置之一运行:作为具有调试输出的命令行工具,作为不接受任何输出的更大程序的一部分,以及在Rails环境中。有时我需要根据代码的位置对代码进行细微的更改,我意识到以下样式似乎可行:print"Testingnestedfunctionsdefined\n"CLI=trueifCLIdeftest_printprint"CommandLineVersion\n"endelsedeftest_printprint"ReleaseVersion\n"endendtest_print()这导致:TestingnestedfunctionsdefinedCommandLin
如何在Ruby中按名称传递函数?(我使用Ruby才几个小时,所以我还在想办法。)nums=[1,2,3,4]#Thisworks,butismoreverbosethanI'dlikenums.eachdo|i|putsiend#InJS,Icouldjustdosomethinglike:#nums.forEach(console.log)#InF#,itwouldbesomethinglike:#List.iternums(printf"%A")#InRuby,IwishIcoulddosomethinglike:nums.eachputs在Ruby中能不能做到类似的简洁?我可以只
//1.验证返回状态码是否是200pm.test("Statuscodeis200",function(){pm.response.to.have.status(200);});//2.验证返回body内是否含有某个值pm.test("Bodymatchesstring",function(){pm.expect(pm.response.text()).to.include("string_you_want_to_search");});//3.验证某个返回值是否是100pm.test("Yourtestname",function(){varjsonData=pm.response.json
说在前面这部分我本来是合为一篇来写的,因为目的是一样的,都是通过独立按键来控制LED闪灭本质上是起到开关的作用,即调用函数和中断函数。但是写一篇太累了,我还是决定分为两篇写,这篇是调用函数篇。在本篇中你主要看到这些东西!!!1.调用函数的方法(主要讲语法和格式)2.独立按键如何控制LED亮灭3.程序中的一些细节(软件消抖等)1.调用函数的方法思路还是比较清晰地,就是通过按下按键来控制LED闪灭,即每按下一次,LED取反一次。重要的是,把按键与LED联系在一起。我打算用K1来作为开关,看了一下开发板原理图,K1连接的是单片机的P31口,当按下K1时,P31是与GND相连的,也就是说,当我按下去时
两个gsub产生不同的结果。谁能解释一下为什么?代码也可在https://gist.github.com/franklsf95/6c0f8938f28706b5644d获得.ver=9999str="\tCFBundleDevelopmentRegion\n\ten\n\tCFBundleVersion\n\t0.1.190\n\tAppID\n\t000000000000000"putsstr.gsub/(CFBundleVersion\n\t.*\.).*()/,"#{$1}#{ver}#{$2}"puts'--------'putsstr.gsub/(CFBundleVersio
我需要一个通过输入字符串进行计算的方法,像这样function="(a/b)*100"a=25b=50function.something>>50有什么方法吗? 最佳答案 您可以使用instance_eval:function="(a/b)*100"a=25.0b=50instance_evalfunction#=>50.0请注意,使用eval本质上是不安全的,尤其是当您使用外部输入时,因为它可能包含注入(inject)的恶意代码。另请注意,a设置为25.0而不是25,因为如果它是整数a/b将导致0(整数)。
假设我在Store的模型中有这个非常简单的方法:defgeocode_addressloc=Store.geocode(address)self.lat=loc.latself.lng=loc.lngend如果我想编写一些不受地理编码服务影响的测试脚本,这些脚本可能已关闭、有限制或取决于我的互联网连接,我该如何模拟地理编码服务?如果我可以将地理编码对象传递到该方法中,那将很容易,但我不知道在这种情况下该怎么做。谢谢!特里斯坦 最佳答案 使用内置模拟和stub的rspecs,你可以做这样的事情:setupdo@subject=MyCl