分享

R语言管道符(%>%)及占位(.)的简单介绍...

 imtravelinghah 2022-10-18 发布于广西

前言

  刚开始学习R语言的时候,经常模仿别人的代码,囫囵吞枣。如今,自己研究的领域中,经常使用的操作也相对熟练,所以,需要静心补一补脑子里的洞,简单整理一下R语言 Magrittr 相关的管道操作。在代码整洁和可维护性方面,Magrittr 管道符(%>%)作用显著😑

一个简单的例子

  刚开始使用R的时候,我为了减少赋值的频次,就把好多个函数套一起使用,经常少了某个括号,看起来也有些”奇怪“,或者说复杂、难理解…比如:

round(cos(exp(sin(log10(sqrt(25))))), 2)
# -0.33

  后来,偶然看见师兄的代码,发现了这个符号%>%,才认识了管道符(开始用bash管道符的时候,想过为啥R没有,后来才知道是我太菜…),有了管道符之后就有了下面这样的操作:

library(magrittr)

sqrt(25) %>%
  log10() %>%
  sin() %>%
  exp() %>%
  cos() %>%
  round(2)
# -0.33

  这样看起来,是不是更好理解、更整洁?那么,管道符做了什么,比如%>%,它把前边的输出结果,呈递给下一个函数,作为输入,例子中就是把sqrt(25)的结果递给log10(),类推。所以,管道符做的,就是把其左边的结果递给右边,作为输入。这会使代码更加的整洁、可读,并且维护性也会提高。

管道符及占位、花括号 {} 、美元符号$

占位

  很多时候,我们使用的函数,数据输入的位置并不是第一个参数,或者,我想要放到我想放的位置,比如gsub等,这类函数在管道符右边的时候,就需要一个东西,指定前面(管道符左边)的输出该放在后面函数的哪个位置。这个东西就是:.。没错,就是这个句点,这样用:

argument2 %>% function(argument1, .)
argument1 %>% function(argument2)

例子:

1:5 %>%
  paste(., letters[.])
  
# [1] "1 a" "2 b" "3 c" "4 d" "5 e"

# install.packages("gapminder")
library(gapminder)
gapminder %>%
  dplyr::pull(continent) %>%
  gsub("Europe", "EUROPE", .) %>%
  head(20)
# [1] "Asia"   "Asia"   "Asia"   "Asia"   "Asia"   "Asia"   "Asia"   "Asia"  
# [9] "Asia"   "Asia"   "Asia"   "Asia"   "EUROPE" "EUROPE" "EUROPE" "EUROPE"
# [17] "EUROPE" "EUROPE" "EUROPE" "EUROPE"

先上一段代码:

gapminder %>%
  dplyr::filter(continent == "Asia") %>%
  dplyr::pull(gdpPercap) %>% round(2) %>% 
  head(10)

# [1] 779.45 820.85 853.10 836.20 739.98 786.11 978.01 852.40 649.34 635.34

  针对数据的continent 列,筛选出包含Asia的数据,然后拿出gdpPercap列,保留两位小数,然后取前十个。很简单的一段代码,但是如果为了缩短函数,像下面这样,把简单的函数head()round()嵌套在一起,得到了不一样的结果:

gapminder %>%dplyr::filter(continent == "Asia") %>%
  dplyr::pull(gdpPercap) %>% 
  head(round(2), 10)

# [1] 779.4453 820.8530

gapminder %>%
  dplyr::filter(continent == "Asia") %>%
  dplyr::pull(gdpPercap) %>% 
  head(., round(2), 10)

# [1] 779.4453 820.8530

  看了head(., round(2), 10),应该就会明白上边代码的输出。首先,从管道符左边过来的数据,并没有直接作为round函数的输入,而是给了head函数。第二点,round函数有两个参数,第一个位输入的vector,第二位是整数,用来指定保留的数位,默认是0,round(2) 返回的结果是2。head函数只接受两个参数,第一位是输入,第二位是指定打印多少,它接收了从管道左边传来的数据,作为第一个参数,round(2)的输出成了它的第二位参数,后面那个10就没用了……所以,一串代码只返回了两个数字,而不是我们想要的效果。想要嵌套一下,可以这样:

gapminder %>%
  dplyr::filter(continent == "Asia") %>%
  dplyr::pull(gdpPercap) %>% 
  head(round(., 2), 10)

# [1] 779.45 820.85 853.10 836.20 739.98 786.11 978.01 852.40 649.34 635.34

在使用管道符的时候,需要特别注意数据输入的位置,合适正确的使用.占位

all_equal(
  gapminder %>%
    dplyr::filter(continent == "Asia") %>%
    dplyr::pull(gdpPercap) %>% 
    round(2) %>% 
    head(10),

  gapminder %>%
    dplyr::filter(continent == "Asia") %>%
    dplyr::pull(gdpPercap) %>% 
    {head(round(., 2), 10)}
)

[1] TRUE

花括号 {}

不多说,直接上代码:

gapminder %>%
  dplyr::filter(continent == "Asia") %>% 
  stats::cor(.$lifeExp, .$gdpPercap)
  
gapminder %>%
      dplyr::filter(continent == "Asia") %>% 
      stats::cor(lifeExp, gdpPercap)

报错:
Error in if (is.na(na.method)) stop(“invalid 'use’ argument”) : the condition has length > 1
Error in pmatch(use, c(“all.obs”, “complete.obs”, “pairwise.complete.obs”, : object 'gdpPercap’ not found

加上花括号:

gapminder %>%
  dplyr::filter(continent == "Asia") %>% 
  {stats::cor(.$lifeExp, .$gdpPercap)}
# Equivalent to the code above
gapminder %>%
  dplyr::filter(continent == "Asia") -> only_asia
stats::cor(only_asia$lifeExp, only_asia$gdpPercap)

  不报错。相对比较容易理解,stats::cor接受两个参数,我们想要的是.$lifeExp, .$gdpPercap,但是管道左边来的数据占了一个位置,并且还是个data.frame,自然就报错了,加上花括号 {},告诉管道符,我不想按照默认的,把左边的输出给右边,这样,stats::cor就只接受.$lifeExp, .$gdpPercap,正常运行。

美元符号 $

两段代码:

library(dplyr)

gapminder %>%
  dplyr::group_by(continent, year) %>%
  dplyr::summarise(count = n()) %>%
  dplyr::mutate(total = sum(count),
                prop = count / total) %>%
  head()

# continent     year    count   total   prop
# Africa    1952    52    624    0.08333333
# Africa    1957    52    624    0.08333333
# Africa    1962    52    624    0.08333333
# Africa    1967    52    624    0.08333333
# Africa    1972    52    624    0.08333333
# Africa    1977    52    624    0.08333333

gapminder %>%
  dplyr::group_by(continent, year) %>%
  dplyr::summarise(count = n()) %>%
  dplyr::mutate(total = sum(.$count),
                prop = count / total) %>%
  head()

# continent     year    count   total   prop
# Africa    1952    52    1704    0.03051643
# Africa    1957    52    1704    0.03051643
# Africa    1962    52    1704    0.03051643
# Africa    1967    52    1704    0.03051643
# Africa    1972    52    1704    0.03051643
# Africa    1977    52    1704    0.03051643

  第一段代码的输出并不是我想要的结果,第二段代码才是。.$允许我使用初始的数据也就是gapminder来计数,而不是dplyr::group_by分组之后的分组数据。所以,在使用dplyr::group_by之后,要注意我们的目的是什么,合理地使用.$,得到期望的结果。

其他管道符 %<>%、%T>%、%$%

对于其他的管道符,这里就举几个例子,不再一一展开讨论了

%<>%

mtcars <- mtcars %>% 
  transform(cyl = cyl * 2)

mtcars %<>% transform(cyl = cyl * 2)

对数据集mtcars操作,输出又赋给了变量mtcars

%T>%

rnorm(200) %>%
matrix(ncol = 2) %T>%
plot %>% # plot usually does not return anything. 
colSums
# [1]  -4.018676 -27.018219

分支操作,随机生成200个数组成两列的矩阵之后,数据分别流向了两个方向,一是画个图,二是算一下列的和

%$%

iris %>%
  subset(Sepal.Length > mean(Sepal.Length)) %$%
  cor(Sepal.Length, Sepal.Width)

mtcars %$%
  cor(disp, mpg)

这个看起来和上边的花括号{}类似,但是并不一样,它把前面的数据暴露出来,可以任意使用数据中变量,而不需要.$获取。

总结

  在使用管道操作的时候,要理解管道的输出形式,正确的使用占位等……不罗嗦了,慢慢探索吧😑

参考

  https://magrittr./

    本站是提供个人知识管理的网络存储空间,所有内容均由用户发布,不代表本站观点。请注意甄别内容中的联系方式、诱导购买等信息,谨防诈骗。如发现有害或侵权内容,请点击一键举报。
    转藏 分享 献花(0

    0条评论

    发表

    请遵守用户 评论公约

    类似文章 更多