Tidyverse数据整理

Author

Lee

Published

August 12, 2025

Important

数据整理的终极目的,就是将原始数据转化为tidy-data的格式,从而方便我们进行后续的分析和可视化。

pacman::p_load(tidyverse, janitor)
airquality <- as_tibble(airquality)
airquality
# A tibble: 153 × 6
   Ozone Solar.R  Wind  Temp Month   Day
   <int>   <int> <dbl> <int> <int> <int>
 1    41     190   7.4    67     5     1
 2    36     118   8      72     5     2
 3    12     149  12.6    74     5     3
 4    18     313  11.5    62     5     4
 5    NA      NA  14.3    56     5     5
 6    28      NA  14.9    66     5     6
 7    23     299   8.6    65     5     7
 8    19      99  13.8    59     5     8
 9     8      19  20.1    61     5     9
10    NA     194   8.6    69     5    10
# ℹ 143 more rows

airquality数据集是一个宽表格,每一天都对应了4个不同的天气变量,但在数据分析中,我们需要将宽表格转换为长表格。长表格是一个更适合数据分析的格式,因为它可以更容易地进行数据操作和可视化。

宽表格容易被阅读,是最常见的数据储存形式。

1 数据变形

1.1 宽边长

gathered <- airquality |>
  pivot_longer(
    cols = c(Ozone, Solar.R, Wind, Temp),
    names_to = "variable",
    values_to = "value"
  )
gathered
# A tibble: 612 × 4
   Month   Day variable value
   <int> <int> <chr>    <dbl>
 1     5     1 Ozone     41  
 2     5     1 Solar.R  190  
 3     5     1 Wind       7.4
 4     5     1 Temp      67  
 5     5     2 Ozone     36  
 6     5     2 Solar.R  118  
 7     5     2 Wind       8  
 8     5     2 Temp      72  
 9     5     3 Ozone     12  
10     5     3 Solar.R  149  
# ℹ 602 more rows

2 长变宽

gathered |>
  pivot_wider(
    names_from = variable,
    values_from = value
  )
# A tibble: 153 × 6
   Month   Day Ozone Solar.R  Wind  Temp
   <int> <int> <dbl>   <dbl> <dbl> <dbl>
 1     5     1    41     190   7.4    67
 2     5     2    36     118   8      72
 3     5     3    12     149  12.6    74
 4     5     4    18     313  11.5    62
 5     5     5    NA      NA  14.3    56
 6     5     6    28      NA  14.9    66
 7     5     7    23     299   8.6    65
 8     5     8    19      99  13.8    59
 9     5     9     8      19  20.1    61
10     5    10    NA     194   8.6    69
# ℹ 143 more rows

3 数据整理

filter(),select()等函数比较熟悉,不再赘述,这里重点关注此前不太熟悉的函数。

3.1 rename()-重命名列

  • rename()函数用于重命名数据框中的列。它的语法为rename(data, new_name = old_name),其中data是数据框,new_name是新列名,old_name是旧列名。
  • select()中可以同时实现选择列和重命名列的功能,但输出的结果仅能保留所选择的列;使用rename()可以重命名列的同时保留所有列。
msleep |>
  filter(order == "Primates", sleep_total > 10) |>
  rename(total = sleep_total, rem = sleep_rem, cycle = sleep_cycle)
# A tibble: 5 × 11
  name     genus vore  order conservation total   rem cycle awake brainwt bodywt
  <chr>    <chr> <chr> <chr> <chr>        <dbl> <dbl> <dbl> <dbl>   <dbl>  <dbl>
1 Owl mon… Aotus omni  Prim… <NA>          17     1.8 NA      7    0.0155   0.48
2 Patas m… Eryt… omni  Prim… lc            10.9   1.1 NA     13.1  0.115   10   
3 Macaque  Maca… omni  Prim… <NA>          10.1   1.2  0.75  13.9  0.179    6.8 
4 Slow lo… Nyct… carni Prim… <NA>          11    NA   NA     13    0.0125   1.4 
5 Potto    Pero… omni  Prim… lc            11    NA   NA     13   NA        1.1 

3.2 列分裂和列融合

3.2.1 分裂

conservation <- read_csv("D:/Myblog/datas/conservation.csv")
conservation
# A tibble: 11 × 1
   `conservation abbreviation`                  
   <chr>                                        
 1 EX = Extinct                                 
 2 EW = Extinct in the wild                     
 3 CR = Critically Endangered                   
 4 EN = Endangered                              
 5 VU = Vulnerable                              
 6 NT = Near Threatened                         
 7 LC = Least Concern                           
 8 DD = Data deficient                          
 9 NE = Not evaluated                           
10 PE = Probably extinct (informal)             
11 PEW = Probably extinct in the wild (informal)

conservation有两个违反tidy原则的地方:

  1. 列名不符合tidy原则,数据中conservation abbreviation应该为两个单独的列。
  2. 数据的值用”=“连接,包含两块内容对应列名中的两部分。
  3. 我们需要将列分裂成多个列,并将列名转换为tidy格式。
conservation |>
  separate(
    `conservation abbreviation`,
    into = c("abbreviation", "description"),
    sep = "="
  )
# A tibble: 11 × 2
   abbreviation description                               
   <chr>        <chr>                                     
 1 "EX "        " Extinct"                                
 2 "EW "        " Extinct in the wild"                    
 3 "CR "        " Critically Endangered"                  
 4 "EN "        " Endangered"                             
 5 "VU "        " Vulnerable"                             
 6 "NT "        " Near Threatened"                        
 7 "LC "        " Least Concern"                          
 8 "DD "        " Data deficient"                         
 9 "NE "        " Not evaluated"                          
10 "PE "        " Probably extinct (informal)"            
11 "PEW "       " Probably extinct in the wild (informal)"

3.3 融合

  • unite()函数用于将多个列合并为一个列。它的语法为unite(data, new_col, col1, col2, sep = "_"),其中data是数据框,new_col是新列名,col1col2是要合并的列名,sep是分隔符。
conservation |>
  separate(
    `conservation abbreviation`,
    into = c("abbreviation", "description"),
    sep = "="
  ) |>
  unite(uited_col, abbreviation, description, sep = "=")
# A tibble: 11 × 1
   uited_col                                    
   <chr>                                        
 1 EX = Extinct                                 
 2 EW = Extinct in the wild                     
 3 CR = Critically Endangered                   
 4 EN = Endangered                              
 5 VU = Vulnerable                              
 6 NT = Near Threatened                         
 7 LC = Least Concern                           
 8 DD = Data deficient                          
 9 NE = Not evaluated                           
10 PE = Probably extinct (informal)             
11 PEW = Probably extinct in the wild (informal)

3.4 数据连接

  • 如果第二个数据集中没有与第一个数据集中相匹配的信息,left_join()函数将会添加NA值。
conserve <- conservation |>
  separate(
    `conservation abbreviation`,
    into = c("abbreviation", "description"),
    sep = " = "
  )
msleep |>
  mutate(conservation = toupper(conservation)) |>
  left_join(conserve, by = c("conservation" = "abbreviation"))
# A tibble: 83 × 12
   name   genus vore  order conservation sleep_total sleep_rem sleep_cycle awake
   <chr>  <chr> <chr> <chr> <chr>              <dbl>     <dbl>       <dbl> <dbl>
 1 Cheet… Acin… carni Carn… LC                  12.1      NA        NA      11.9
 2 Owl m… Aotus omni  Prim… <NA>                17         1.8      NA       7  
 3 Mount… Aplo… herbi Rode… NT                  14.4       2.4      NA       9.6
 4 Great… Blar… omni  Sori… LC                  14.9       2.3       0.133   9.1
 5 Cow    Bos   herbi Arti… DOMESTICATED         4         0.7       0.667  20  
 6 Three… Brad… herbi Pilo… <NA>                14.4       2.2       0.767   9.6
 7 North… Call… carni Carn… VU                   8.7       1.4       0.383  15.3
 8 Vespe… Calo… <NA>  Rode… <NA>                 7        NA        NA      17  
 9 Dog    Canis carni Carn… DOMESTICATED        10.1       2.9       0.333  13.9
10 Roe d… Capr… herbi Arti… LC                   3        NA        NA      21  
# ℹ 73 more rows
# ℹ 3 more variables: brainwt <dbl>, bodywt <dbl>, description <chr>

3.5 汇总函数

  • janitor::tabyl():创建一个频率表(类似table()但输出更整洁的数据框),第一个参数是向量/数据框列,可选的第二个参数是交叉分类变量。如果需要对数字型变量进行统计,使用summary()函数。

  • janitor::get_dupes():查找并返回数据框中的重复行,可以指定列来检查重复项,未指定则检查所有列

  • dplyr::count(): 计算分组观测值数量并返回新数据框,相当于 group_by() + summarize(n = n()) 的快捷方式

  • dplyr::add_count():添加分组计数列但不聚合数据,保留原始行数,新增列显示每行所在组的计数

  • dplyr::tally():对数据计数(类似count但语法更简单),通常与group_by()配合使用,或直接对整体计数

  • dplyr::add_tally():添加总数列但不聚合数据,保留原始行数,新增列显示每行所在组的计数

  • skimr::skim(): 生成全面的数据摘要统计信息,对每列提供均值、分位数、直方图等丰富统计量

msleep |>
  janitor::tabyl(order, vore)
           order carni herbi insecti omni NA_
    Afrosoricida     0     0       0    1   0
    Artiodactyla     0     5       0    1   0
       Carnivora    12     0       0    0   0
         Cetacea     3     0       0    0   0
      Chiroptera     0     0       2    0   0
       Cingulata     1     0       1    0   0
 Didelphimorphia     1     0       0    1   0
   Diprotodontia     0     1       0    0   1
  Erinaceomorpha     0     0       0    1   1
      Hyracoidea     0     2       0    0   1
      Lagomorpha     0     1       0    0   0
     Monotremata     0     0       1    0   0
  Perissodactyla     0     3       0    0   0
          Pilosa     0     1       0    0   0
        Primates     1     1       0   10   0
     Proboscidea     0     2       0    0   0
        Rodentia     1    16       0    2   3
      Scandentia     0     0       0    1   0
    Soricomorpha     0     0       1    3   1

3.6 多列同时操作-across()

across()函数用于对多个列进行操作,通常与mutate()summarise()等函数结合使用。它的语法为across(.cols, .fns),其中.cols是要操作的列,.fns是要应用的函数。

更多可参看Tidyverse包中的across()函数

4 因子处理

  • fct_relevel(): 重新排列因子水平的顺序,fct_relevel(factor, new_levels, after = 0L),其中factor是因子变量,new_levels是新的水平顺序,after是插入新水平的位置,默认为0L,即在最前面。

  • fct_inorder(),fct_infreq(),fct_inseq(): 按照因子水平的出现顺序/频数/顺序数字重新排列因子水平,fct_inorder(factor),其中factor是因子变量。

  • fct_rev(): 反转因子水平的顺序,fct_rev(factor),其中factor是因子变量。