R语言学习笔记_02

字符串操作

主要介绍R基础函数以及stringr包中对应函数

  • paste, paste0连接两个字符串,其中paste0的默认分隔符是"";在stringr中使用str_c(collapse=)指定分隔符合并字符串。
# 需要注意一般配合管道函数%>% 使用,只需要指定collapse参数(str_c)或sep(paste)即可
paste('abc', 'def', sep = '')
#等同于paste0('abc', 'def')
#等同于str_c('abc', 'def', collapse = '')
[1] "abcdef"
  • nchar计算字符串中字符个数,同str_length
    nchar()能够获取字符串的长度,它和length()的结果是有区别的。
# 计算每一个字符串长度
nchar(c("abc", "abcd"))    #求字符串中的字符个数,返回向量c(3, 4)
# 计算元素个数
length(c("abc", "abcd"))  #返回2,向量中元素的个数
[1] 3 4

[2] 2
  • substrsubstring提取指定子字符串,substr按开始和结束位置获取字符串,支持输入多个字符串,使用c()传入,
    substr返回的字串个数等于第一个参数的长度,而substring返回字串个数等于三个参数中最长向量长度,短向量循环使用。
# 提取起始位置为2终止位置为4的子字符串
substr("abcdef", 2, 4)
# 也可以传入多个字符串
substr(c("abcdef", "ghijkl"), 2, 4)

# 而对于substring函数则将两个参数向量中对应位置组合并输出切片字符串,两个向量长度不同时短向量循环使用
substring("abcdef",1:2,5) # 输出1:5和2:5的两个子字符串
substring("abcdef",1:6,1:6) # 将字符串分割为单个字符
[1] "bcd"

[2] "bcd" "hij"

[3] "abcde" "bcde"

[4] "a" "b" "c" "d" "e" "f"
  • strsplit按指定分隔符将字符串分隔,同str_split,返回一个列表
# R中内置函数输出结果为一个列表
strsplit(c('abc.def', 'aaa.bbb'), '[.]')
# 等同于str_split(c('abc.def', 'aaa.bbb'), '[.]'),如果设置参数simplify = T则输出一个数据框
str_split(c('abc.def', 'aaa.bbb'), '[.]', simplify = T)
[[1]]
[1] "abc" "def"

[[2]]
[1] "aaa" "bbb"
     [,1]  [,2] 
[1,] "abc" "def"
[2,] "aaa" "bbb"
  • tolowertoupper转换大小写,同str_to_upper转换大写,str_to_lower转换小写,str_to_title首字母大写

  • grepgrepl搜索某个模式的子串,主要关注两个参数patternx,第一个指定需要匹配的字符串模式,第二个参数数据。grep在字符串向量x中查找一个正则表达式pattern,返回包括这个pattern的字符串的索引。grepl等同于stringr中的str_detect用于检测字符串是否存在某些指定模式,返回bool值,存在该模式返回True,不存在返回False。

# grep匹配指定字符串随后返回其index
grep("def", c("abc","abcdef","def"))    #第2、3两个字符串中都包含def,所以返回2和3

# grepl不返回index而是返回bool值
grepl("def", c("abc","abcdef","def"))
# 等同于str_detect("def", c("abc","abcdef","def"))
[1] 2 3

[2] FALSE  TRUE  TRUE
  • subgsub搜索并替换字符串。gsub与sub类似但替换所有符合模式的字符串。同stringr中的str_replacestr_replace_all
text <- c("aeweqe", "sdfeqewew")
# 仅替换第一个匹配的字符
sub("e", "E", text)
# 替换所有匹配的字符
gsub("e","E",text)
[1] "aEweqe"    "sdfEqewew"

[2] "aEwEqE"    "sdfEqEwEw"
  • regexprgregexpr找到匹配的字符串的位置。gregexpr与regexpr类似但查找所有匹配的位置,主要使用两个参数,第一个参数指定需要匹配的pattern,第二个参数指定数据x。在stringr中等同于str_locatestr_locate_all,类似于python中的find

需要注意,在stringr中的str_locate函数第一个参数是指定数据x,而第二个参数才是需要匹配的pattern,这里和regexpr是正好相反的。并且str_locate的返回值也是不同的,str_locate返回一个matrix,里面包括了匹配的起始位置和终止位置,而str_locate_all返回一个list,包含所有匹配到的位置的起始位置和终止位置。

# 返回匹配的起点,符合模式的长度等信息
regexpr("def", "abcdefghidef")    #def从第4个字符位置上出现
str_locate("abcdefghidef", "def")

# 返回一个list包含所有匹配的位置信息
gregexpr("def", "abcdefghidef")
str_locate_all("abcdefghidef", "def")
[1] 4
attr(,"match.length")
[1] 3
attr(,"index.type")
[1] "chars"
attr(,"useBytes")
[1] TRUE
     start end
[1,]     4   6
[[1]]
[1]  4 10
attr(,"match.length")
[1] 3 3
attr(,"index.type")
[1] "chars"
attr(,"useBytes")
[1] TRUE
[[1]]
     start end
[1,]     4   6
[2,]    10  12

  • trimws删除字符串指定位置的指定符号,可以指定c("both", "left", "right")以及需要删除的符号[ \t\r\n],在stringr中也可以使用str_trim(string, side = c("both", "left", "right"))实现该功能,同样可以指定左边,右边或者两边都去除。
# 去除空格
> str_trim("  String with trailing and leading white space\t")

# 去除换行符,制表符等符号
> str_trim("\n\nString with trailing and leading white space\n\n")
[1] "String with trailing and leading white space"

[2] "String with trailing and leading white space"
  • starts_withends_withcontains这些函数允许根据变量的名称选择变量。starts_with匹配字符串是否以某种模式开头;ends_with匹配字符串是否以某种模式结尾;contains指定字符串中是否包含某种模式。类似于python中的startswithendswith。但是和python中的函数不同的是它们不返回逻辑值而是直接返回与其匹配的字符串的坐标。
nms <- names(iris)
starts_with("Petal", nms, ignore.case = TRUE)
[1] 3 4
  • sprintf这个函数与C语言的函数类似,同样类似于python中的字符串格式化,使用%d%.*f%s分别指定整型,浮点型和字符串类型
# 可以使用%占位符输入字符串s,浮点数f以及整型d
sprintf("%d, %.2f, %s", 12, 1.333, 'fff') # 类似于python中的字符串格式化

# 如果还想要操作多个字符串,还需要配合paste(str_c)函数进行操作
print(paste0(sprintf('this is a tmp: %d', 123), sprintf('%s !!!', 'end')))
# 在python中:print('this is a tmp: %d %s !!!' % (123, 'end'))
[1] "12, 1.33, fff"

[2] "this is a tmp: 123end !!!"

apply家族函数

在R语言的使用中一般情况下applylapplysapply可以胜任大部分重复性编码从而避免使用for循环

  • apply在apply函数中数据通常是矩阵,输入三个参数,第一个参数指定数据,第二个参数指定轴,1代表行,2代表列,我们希望对行进行计算或者对列进行计算就需要指定该参数,第三个参数指定想要实现的功能,需要注意至多只能有一个参数,如果想要执行的函数具有多个参数则需要为其指定默认值
df <- matrix(c(4, 9, 16, 25, 36, 49), 2, 3)

# 对行操作,2*3矩阵按行求和输出两个数值
apply(df, 1, sum)
# 对列操作,2*3矩阵按列求和输出3个数值
apply(df, 2, sum)
[1] 56 83

[2] 13 41 85
  • lapply即list_apply,lapply的特殊之处在于,它的输出形式为列表(list),并且每一个运算结果(数值)都是一个单独的列表。这就是说,如果运算结果有六个数值,lapply就会输出六个列表,每一个列表里为一个数值。
df <- matrix(c(4, 9, 16, 25, 36, 49), 2, 3)
lapply(df, sqrt)
[[1]]
[1] 2

[[2]]
[1] 3

[[3]]
[1] 4

[[4]]
[1] 5

[[5]]
[1] 6

[[6]]
[1] 7

lapply比较常用的一个原因是我们在执行lapply的时候即可以预先对结果输出形式进行设计,使用do.call函数对lapply的输出结果进行整理

# 使用do.call设计输出模式
do.call(cbind, lapply(df, sqrt))

# 或者直接转换为矩阵
data.frame(lapply(df, sqrt))
     [,1] [,2] [,3] [,4] [,5] [,6]
[1,]    2    3    4    5    6    7
  X2 X3 X4 X5 X6 X7
1  2  3  4  5  6  7
  • sapply是lapply的变种,sapply函数运算方法与lapply完全相同,唯一的区别是sapply输出形式并不是列表(list),而是“数值”。直观地看一下R中sapply的运算结果:
df <- matrix(c(4, 9, 16, 25, 36, 49), 2, 3)
sapply(df, sqrt) # 输出一维向量
[1] 2 3 4 5 6 7

面向对象

在R中具有S3S4RCR6等四套标准用于实现OOP范式,其中关于S3、S4两种范式在早期的各种扩展包中使用比较多,是基于泛型函数而实现的,在bioconductor中全部使用S4对象。在这里主要介绍一下S4对象以及RC对象。

S4对象

首先需要显式的定义过程setClass在其中我们需要指定属性和属性类型,其主要参数如下:

  • Class:定义类名
  • slots:定义属性和属性类型
  • prototype:定义属性的默认值
  • contains=character():定义父类,继承关系
  • validity:定义属性的类型检查
  • where:定义存储空间
  • sealed:如果设置TRUE,则同名类不能再次定义
  • package:定义所属的包
  • S3methods:R3.0.0后不建议使用
  • representation:R3.0.0后不建议使用
  • access:R3.0.0后不建议使用
  • version:R3.0.0后不建议使用
# 创建一个S4对象Person,prototype设定默认值
setClass("Person",slots=list(name="character",age="numeric"),prototype=list(age=20))

# 创建Person的子类
setClass("Son",slots=list(father="Person",mother="Person"),contains = "Person")

# 实例化Person对象
father <- new("Person",name="F",age=44)
mother <- new("Person",name="M",age=39)

# 实例化一个Son对象
son <- new("Son",name="S",age=16,father=father,mother=mother)

# S4对象,还支持从一个已经实例化的对象中创建新对象,创建时可以覆盖旧对象的值
n1 <- new("Person",name="n1",age=19)
# 从实例n1中,创建实例n2,并修改name的属性值
n2 <- initialize(n1,name="n2")

# 查看son对象的name属性
son@name
[1] "S"

S4的泛型函数实现有别于S3的实现,S4分离了方法的定义和实现,如在其他语言中我们常说的接口和实现分离。通过setGeneric()来定义接口,通过setMethod()来定义现实类。这样可以让S4对象系统,更符合面向对象的特征。

# S4对象的类型检查
setValidity("Person",function(object){
  if(object@age <= 0) stop("Age is negative.")
})

# 定义泛型函数work即接口
setGeneric("work",function(object) standardGeneric("work"))
# 定义work的实现,并指定参数类型为Person对象
setMethod("work",signature(object="Person"),function(object) cat(object@name,"is working"))
[1] "work"

通过S4对象系统,把原来的函数定义和调用2步完成的分成4步。

  • 定义数据对象类型
  • 定义接口函数
  • 定义实现函数
  • 把数据对象以参数传入到接口函数,执行实现函数
    通过S4对象系统,是一个结构化的,完整的面向对象的实现。

RC对象

首先在介绍RC对象之前需要简单介绍一下R的赋值符号,在R中<-->是一对,可以向左和向右赋值,它们的赋值行为均在它们自身的环境层;=是单向的,作用和<-基本相同,但对函数中的变量通常使用=<<-处在某一个环境层的代码都拥有读入上一环境层的变量的权限,但相反地,若只通过标准的赋值运算符<-,是无法向上一环境层写入变量的。若想在上一环境层进行赋值行为,即向上一层次写入变量,则需要用到<<-(superassignment)运算符,在类中由于没有self区分类属性,所以就需要<<-对类的属性进行操作

RC(Reference Class)对象系统从底层上改变了原有S3和S4对象系统的设计,去掉了泛型函数,其真正的以类为基础实现面向对象的特征。RC对象看起来类似于python中的class类,在setRefclass中我们需要指定属性,初始化函数,主体函数等。其参数列表:

  • Class: 定义类名
  • fields: 定义属性和属性类型
  • contains:定义父类,继承关系
  • methods: 定义类中的方法
  • where: 定义存储空间

在S3,S4的对象系统中,我们实现对象行为时,都是借助于泛型函数实现的。这种实现方法的最大问题是:在定义函数和对象的代码是分离的,需要在运行时,通过判断对象的类型完成方法的调用。而RC对象系统中,方法可以定义在类的内部,通过实例化的对象完成方法的调用。

User <- setRefClass("User",fields=list(name="character",favorite='vector'),
                    
                    # 方法属性
                    methods= list(

                      # 增加一个兴趣
                      addFavorite = function(x){
                        favorite <<- c(favorite,x)
                      },
                      # 删除一个兴趣
                      delFavorite = function(x){
                        favorite <<- favorite[-which(favorite==x)]
                      },
                      # 重新定义兴趣列表
                      setFavorite = function(x){
                        favorite <<- x
                      }
                    ))
# 类比python的类,RC类与python的类已经很相似了
class User(object):
    def __init__(self, name:str, favorite:list):
        self.name = name
        self.favorite = favorite

    def addFavorite(self, x):
        self.favorite.append(x)

    def delFavorite(self, x):
        self.favorite.remove(x)

    def setFavorite(self, x):
        self.favorite = x

RC类中的内置方法:

  • initialize类的初始化函数,用于设置属性的默认值,只有在类定义的方法中使用。
  • callSuper调用父类的同名方法,只能在类定义的方法中使用
  • copy复制实例化对象的所有属性
  • initFields给对象的属性赋值
  • field查看属性或给属性赋值
  • getClass查看对象的类定义
  • getRefClass()同getClass()
  • show 查看当前对象
  • export查看属性值以类为作用域
  • import 把一个对象的属性值赋值给另一个对象
  • trace跟踪对象中方法调用,用于程序debug
  • untrace取消跟踪
  • usingMethods用于实现方法调用,只能在类定义的方法中使用,这个方法不利于程序的健壮性,所以不建议使用。
# 创建父类
User <- setRefClass("User",
                    fields=list(name="character",level="numeric"),
                    methods = list(
                      # 调用类的初始化函数
                      initialize=function(name,level){ 
                        print("User::initialize")
                        name <<- "conan"
                        level <<- 1
                      },
                      addLevel = function(x){
                        print("User::addlevel")
                        level<<-level+x
                      },
                      addHighLevel = function(){
                        print("user::addHighLevel")
                        addLevel(2)
                      }
                    ))


# 子类Member调用callSuper方法重写父类方法,类似python中super
Member <- setRefClass("Member",contains="User",
                      # 子类中的属性
                      fields = list(age='numeric'),
                      methods=list(
                        
                        # 覆盖父类的同名方法
                        addLevel = function(x){
                          print("Member::addLevel")
                          callSuper(x)
                          level <<- level+1 
                        }
                      )
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 160,165评论 4 364
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,720评论 1 298
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 109,849评论 0 244
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 44,245评论 0 213
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,596评论 3 288
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,747评论 1 222
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,977评论 2 315
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,708评论 0 204
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,448评论 1 246
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,657评论 2 249
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,141评论 1 261
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,493评论 3 258
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,153评论 3 238
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,108评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,890评论 0 198
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,799评论 2 277
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,685评论 2 272