R语言ETL系列:创建字段(mutate)

浏览: 186

作者:黄天元,复旦大学博士在读,目前研究涉及文本挖掘、社交网络分析和机器学习等。希望与大家分享学习经验,推广并加深R语言在业界的应用。

邮箱:huang.tian-yuan@qq.com

有没有一些时候你想要给表格加一个新的列?本章将会介绍如何在表格中创建新的字段。但是首先我们要知道为什么要什么做,下面是可能的一些情况:

  • 有时候需要构造一列全部是0或1的数列,或者是给每一个记录赋予一个不同的ID;

  • 购物数据中,含有单价和数量,需要计算交易额,然后加入到表格中;

  • 我们数据中有年月日的数据,我们需要把它们拼接成一个字符格式,比如,2018年10月10日,我们需要表示为20181010或者2018/10/10;

在tidyverse中,我们可以用mutate函数给表格增加新的一列。下面将会分节介绍,首先载入我们需要的数据和包:

1library(tidyverse)
2library(nycflights1

添加常数列

这里举一个简单的例子,用iris数据集,增加一个完全为0的新列,我们会限制输出列为前六行,以免输出内容过多。

 1iris %>%
2  mutate(zeros = 0) %>%
3  head
4##   Sepal.Length Sepal.Width Petal.Length Petal.Width Species zeros
5## 1          5.1         3.5          1.4         0.2  setosa     0
6## 2          4.9         3.0          1.4         0.2  setosa     0
7## 3          4.7         3.2          1.3         0.2  setosa     0
8## 4          4.6         3.1          1.5         0.2  setosa     0
9## 5          5.0         3.6          1.4         0.2  setosa     0
10## 6          5.4         3.9          1.7         0.4  setosa     0

可以看到我们已经新增了一列,名字叫做zeros,里面的值全部是0。 SQL代码如下:

1<SQL> SELECT `Sepal.Length``Sepal.Width``Petal.Length``Petal.Width``Species`0.0 AS `zeros`
2FROM `iris`

算数运算

本例会用到flights数据,这个表中包含了航班的起飞时间(dep_time)和到达时间(arr_time),如果需要知道中间经理了多长时间,我们需要将两者相减。我们会先把两列取出来,然后再构造新的列:

 1flights %>%
2  select(dep_time,arr_time) %>%
3  mutate(time = arr_time - dep_time)
4## # A tibble: 336,776 x 3
5##    dep_time arr_time  time
6##       <int>    <int> <int>
7##  1      517      830   313
8##  2      533      850   317
9##  3      542      923   381
10##  4      544     1004   460
11##  5      554      812   258
12##  6      554      740   186
13##  7      555      913   358
14##  8      557      709   152
15##  9      557      838   281
16## 10      558      753   195
17## # ... with 336,766 more rows

这样一来我们就把飞行时间储存在time列中。事实上我们还可以这么表达:

 1flights %>%
2  transmute(dep_time,arr_time,time = arr_time - dep_time)
3## # A tibble: 336,776 x 3
4##    dep_time arr_time  time
5##       <int>    <int> <int>
6##  1      517      830   313
7##  2      533      850   317
8##  3      542      923   381
9##  4      544     1004   460
10##  5      554      812   258
11##  6      554      740   186
12##  7      555      913   358
13##  8      557      709   152
14##  9      557      838   281
15## 10      558      753   195
16## # ... with 336,766 more rows

得到结果与上面一致。transmute函数的特点就是,不会保存数据表中原有的列,自动完成了select的功能。
SQL代码如下:

1<SQL> SELECT `dep_time``arr_time``arr_time` - `dep_time` AS `time`
2FROM (SELECT `dep_time``arr_time`
3FROM `flights`)

文本的拼接与拆分

这个例子中,我们来看flights数据,它包含了年(year)、月(month)、日(day)、起始点(origin)、终点(dest)的数据列,首先我们把这些列取出来存放到新的变量中,并进行观察:

 1flights %>%
2  select(year,month,day,origin,dest) -> fl_ymd
3
4fl_ymd
5## # A tibble: 336,776 x 5
6##     year month   day origin dest 
7##    <int> <int> <int> <chr>  <chr>
8##  1  2013     1     1 EWR    IAH  
9##  2  2013     1     1 LGA    IAH  
10##  3  2013     1     1 JFK    MIA  
11##  4  2013     1     1 JFK    BQN  
12##  5  2013     1     1 LGA    ATL  
13##  6  2013     1     1 EWR    ORD  
14##  7  2013     1     1 EWR    FLL  
15##  8  2013     1     1 LGA    IAD  
16##  9  2013     1     1 JFK    MCO  
17## 10  2013     1     1 LGA    ORD  
18## # ... with 336,766 more rows

现在,我们想要把第一行的起始点和终点拼接为“EWR_IAH”的格式,放在名为origin_dest的列中,下面我们进行操作:

 1fl_ymd %>%
2  mutate(origin_dest = str_c(origin,dest,sep = "_")) -> fl_ymd.1
3
4fl_ymd.1
5## # A tibble: 336,776 x 6
6##     year month   day origin dest  origin_dest
7##    <int> <int> <int> <chr>  <chr> <chr>      
8##  1  2013     1     1 EWR    IAH   EWR_IAH    
9##  2  2013     1     1 LGA    IAH   LGA_IAH    
10##  3  2013     1     1 JFK    MIA   JFK_MIA    
11##  4  2013     1     1 JFK    BQN   JFK_BQN    
12##  5  2013     1     1 LGA    ATL   LGA_ATL    
13##  6  2013     1     1 EWR    ORD   EWR_ORD    
14##  7  2013     1     1 EWR    FLL   EWR_FLL    
15##  8  2013     1     1 LGA    IAD   LGA_IAD    
16##  9  2013     1     1 JFK    MCO   JFK_MCO    
17## 10  2013     1     1 LGA    ORD   LGA_ORD    
18## # ... with 336,766 more rows

我们可以看到,使用mutate函数可以增加一列,而这一列通过str_c函数,把两列拼接起来,中间的分隔符号设定为下划线(“_“)。此外,我们可以用unite函数完成这个任务:

 1#等价于
2fl_ymd %>%
3  unite(col = origin_dest,origin,dest) -> fl_ymd.2
4
5fl_ymd.2
6## # A tibble: 336,776 x 4
7##     year month   day origin_dest
8##    <int> <int> <int> <chr>      
9##  1  2013     1     1 EWR_IAH    
10##  2  2013     1     1 LGA_IAH    
11##  3  2013     1     1 JFK_MIA    
12##  4  2013     1     1 JFK_BQN    
13##  5  2013     1     1 LGA_ATL    
14##  6  2013     1     1 EWR_ORD    
15##  7  2013     1     1 EWR_FLL    
16##  8  2013     1     1 LGA_IAD    
17##  9  2013     1     1 JFK_MCO    
18## 10  2013     1     1 LGA_ORD    
19## # ... with 336,766 more rows

unite中的col参数是新的列名称,后面可以放任意多的列,最后会把放进去的列都拼接起来。

unite函数是专门用来对不同列之间字符串进行拼接的函数,默认的间隔符号就是“_”,但是可以通过sep参数改变分隔符。此外,我们可以注意到,如果利用unite函数,原始的两列origin和dest会消失,而mutate则是单纯增加一列。


那么,如果我们想要把已经拼接好的数据拆分出来,应该怎么做呢?
我们现在就来对flm_ymd.2的origin_dest列进行拆分操作,这里我们会用到separate函数:

 1fl_ymd.2 %>%
2  separate(col = origin_dest, into = c("origin","dest"))
3## # A tibble: 336,776 x 5
4##     year month   day origin dest 
5##    <int> <int> <int> <chr>  <chr>
6##  1  2013     1     1 EWR    IAH  
7##  2  2013     1     1 LGA    IAH  
8##  3  2013     1     1 JFK    MIA  
9##  4  2013     1     1 JFK    BQN  
10##  5  2013     1     1 LGA    ATL  
11##  6  2013     1     1 EWR    ORD  
12##  7  2013     1     1 EWR    FLL  
13##  8  2013     1     1 LGA    IAD  
14##  9  2013     1     1 JFK    MCO  
15## 10  2013     1     1 LGA    ORD  
16## # ... with 336,766 more rows

我们可以看到,separate其实是unite的逆运算,col中赋值的是需要拆分的列名称,into则是拆分之后不同部分的名称。 separate也有sep参数,可以调整参数来根据特殊符号进行拆分,默认的拆分参数是所有非字母符号。如果大家要用中文的话,应该注意,一定要使用sep参数,也就是应该写成“fl_ymd.2 %>% separate(col =origin_dest, into = c(”origin“,”dest“),sep =”_“)”。


本章中,我们讲了如何用mutate创建字段。我们可以灵活地使用mutate函数,构造一个常数列,或者基于原始的数据列进行数学运算,从而构成新的列。这些知识对于机器学习中的特征工程尤为重要,是必须掌握的基础。最后,我们还介绍了如何对字符型的变量进行拼接和拆分,如果需要进行文本挖掘的话,这些技能是必不可少的。

往期精彩:

推荐 0
本文由 R语言中文社区 创作,采用 知识共享署名-相同方式共享 3.0 中国大陆许可协议 进行许可。
转载、引用前需联系作者,并署名作者且注明文章出处。
本站文章版权归原作者及原出处所有 。内容为作者个人观点, 并不代表本站赞同其观点和对其真实性负责。本站是一个个人学习交流的平台,并不用于任何商业目的,如果有任何问题,请及时联系我们,我们将根据著作权人的要求,立即更正或者删除有关内容。本站拥有对此声明的最终解释权。

0 个评论

要回复文章请先登录注册