美文网首页
R语言揭秘 | $符鲜为人知的秘密,避坑预警

R语言揭秘 | $符鲜为人知的秘密,避坑预警

作者: 生信云笔记 | 来源:发表于2023-11-03 15:54 被阅读0次

序 言

  神奇,这样操作也可以,瞬间再一次击中内心的好奇心。R语言做为用户友好型的高级语言,使用多了时不时就能像彩蛋一样蹦出个隐藏技能。这不,小伙伴无意中就发现了$符的一个隐藏技能。
  说到$符,熟悉的人都知道可以用来从data.frame里面提取列的数据,很普通的功能。做为R语言的老用户,首次遇到这个隐藏特性时本人也表示一脸疑惑,不会吧?啥情况?出错了?R本版?
  带着这些疑问,咱们一起看看到底是怎么回事?$是如何做到不用完整的列名,也可以提取出正确的列数据。

回 顾

  先用实际例子展示一下事情的真实面貌,下面创建一个数据框来演示:

df1 <- data.frame(gene_id=paste0('gene',1:5), value=1:5, stringsAsFactors=F)
df1
  gene_id value
1   gene1     1
2   gene2     2
3   gene3     3
4   gene4     4
5   gene5     5

df1$gene_id
[1] gene1 gene2 gene3 gene4 gene5

df1$gene
[1] "gene1" "gene2" "gene3" "gene4" "gene5"

df1$g
[1] "gene1" "gene2" "gene3" "gene4" "gene5"

df1$v
[1] 1 2 3 4 5

  数据框df1有两列,列名分别为gene_idvalue。通常,使用$符号从数据框里面取数据都是用完整的列名,比如从df1里面提取gene_id列数据的操作为df1$gene_id。可没想到的是用列名的前几个字符也是可以的,比如df1$genedf1$g。瞬间内心的情绪变得有点复杂,好奇中透漏着点疑惑,疑惑中夹杂着些不安
  不明真相时,这些奇奇怪怪的操作着实带有一种琢磨不透的神秘感与诱惑力。好奇心果然是不错的驱动力,既然遇到了这个问题,带着对代码的小偏执,咱来一探究竟:

df1$count
NULL

df1[,'count']
Error in `[.data.frame`(df1, , "count") : undefined columns selected

  上面的两行代码,都可以从df1里面提取count列的数据,执行后的结果却完全不同。由于df1里面根本没有这一列,[的方式直接报错,这很容易理解,而$方式却毫无波澜的正常执行了。虽然从$方式返回的NULL可以推测df1里面没有count列,但还是不免有些担心的情绪,这要是在复杂的代码里藏着这么个小失误,那多少得折腾一下了。

发 现

  在脑海里闪过不少联想,难道真的是偷懒机制,方便用户少敲些代码?这答案有点道理,可却没法说服自己呀。
  脑海里继续浮想联翩,不由地想起不久前写过的帖子[R编程技巧 | 学习高手实现函数多功能化的两种方法],match.arg可以校验函数参数,$这个特性是不是跟这个有关。回过头一想,$符就是一个函数,内心似乎得到了答案 -- 参数的校验机制。看看下面的代码示例:

df2 <- data.frame(gene_id=paste0('gene',1:5), gene_name=paste0('GENE',1:5), value=1:5, stringsAsFactors=F)
df2
  gene_id gene_name value
1   gene1     GENE1     1
2   gene2     GENE2     2
3   gene3     GENE3     3
4   gene4     GENE4     4
5   gene5     GENE5     5

df2$gene_id
[1] "gene1" "gene2" "gene3" "gene4" "gene5"

df2$gene
NULL

  df2里面有两列gene_idgene_name都含有gene,由于受到干扰此时用gene就无法正确取出数据了,这更有match.arg的感觉了。

答 案

  也许上面的方式并不直观,有些小伙伴不太明白,下面换个直白的方式,用函数的形式来演示一下,保证看完立马觉悟,一起来见证:

# $方式
df2$gene_id
[1] "gene1" "gene2" "gene3" "gene4" "gene5"

df2$gene
NULL

# 正常函数形式
'$'(df2, gene_id)
[1] "gene1" "gene2" "gene3" "gene4" "gene5"

'$'(df2, gene)
NULL

  真相开始浮出水面,$函数会校验参数是不是数据框的列名,如果存在与之匹配的唯一列名即返回数据,否则返回NULL。最后,再来看看$函数的源码:

# 源码
function (x, name)
{
    a <- x[[name]]
    if (!is.null(a))
        return(a)
    a <- x[[name, exact = FALSE]]
    if (!is.null(a) && getOption("warnPartialMatchDollar", default = FALSE)) {
        names <- names(x)
        warning(gettextf("Partial match of '%s' to '%s' in data frame",
            name, names[pmatch(name, names)]))
    }
    return(a)
}

# [[方式
df2[['value']]

df2[['va']]
NULL

df2[['va', exact=F]]
[1] 1 2 3 4 5

  可以看出$函数内部利用了[[方式提取数据,而[[可以通过参数exact参数控制字符是否需要完全匹配。至此,一切好像都明朗了,满足了内心的好奇心。奇怪的知识又增加了。。。

结 语

  R语言易使用的特性,很多时候也是通过语法糖的方式,比如$函数的调用方式。易学实用固然是好的,但其中包含的兼容性还需格外留心,比如,$这种方式失误躲在代码堆里,需要多长时间发现这个BUG?
  在R语言的世界里,有时候自己写得代码能够运行下来不出错,得到的结果也未必正确。可见,虽然R用起来简单,但还是需要了解一些语法特性,唯避坑耳。

往期回顾

scRNA-seq稀疏矩阵图解,格式转换的核心
scRNAseq | h5文件转化为matrix表达矩阵
venn | 多样本间peak重叠韦恩图的解决方案
R编程技巧 | 学习高手实现函数多功能化的两种方法
linux | while + read 实现本地 or 集群批处理,实用且优雅

相关文章

  • 《观止-微软创建NT和未来的夺命狂奔》 读后感

    (来自豆瓣)内容简介: 沉寂15年,首次与中国读者见面,揭秘密世界巨头微软公司成功背后鲜为人知的秘密! 如其说这是...

  • R实用基础2——常用数据处理命令

    总结了一些平时常用的数据处理命令 运算符 R语言运算符 - R语言教程™ (yiibai.com)[https:/...

  • 第2章 R编程入门(二):运算符

    2.1 R语言 2.1.2 R运算符 1. 算数运算符 算数运算符含义^乘幂*乘/除+加-减%%模运算%/%整数除...

  • 第二章 数值、运算、赋值和向量(上)

    一、R语言中拥有如下几种运算符类型: 算术运算符关系运算符逻辑运算符赋值运算符其他运算符 算术运算符下表显示了R语...

  • R语言 运算符(转载)

    R语言 运算符 转自:https://www.w3cschool.cn/r/r_operators.html 运算...

  • 01-R语言入门

    R语言的使用 1、R语言是区分大小写种的解释型语言。在命令提示符(>)后每次输入并执行一条命令。2、R语言包括向量...

  • go 语言开发避坑

    随工作补充,可以作为代码reveiw的注意点。 不建议在循环中使用defer 闭包循环变量 正确写法: 需要判断指...

  • ANSI 转义字符

    ANSI C语言中的全部转义字符序列如下:## a:响铃符b:回退符f:换页符n:换行符r:回车符t:横向制表符v...

  • R语言 运算符

    运算符是一个符号,通知编译器执行特定的数学或逻辑操作。 R语言具有丰富的内置运算符,并提供以下类型的运算符。运算符...

  • R语言填坑记

    受网上帖子启发,想想自己用R语言就是不断跑代码,纠正error,我也记录一下我在用R时遇到的错误。 1.R语言填坑...

网友评论

      本文标题:R语言揭秘 | $符鲜为人知的秘密,避坑预警

      本文链接:https://www.haomeiwen.com/subject/zefvidtx.html