Skip to Tutorial Content

知识点

  • 单数据整理
    • 探索
    • 梳理
    • 筛选
    • 修改
  • 跨数据整理
    • 直接合并
    • 索引合并

演示数据

我们从已故政治学教授Ronald Inglehart创立的World Values Survey第七波数据(WVS7)的一个样本进行演示。 这个样本是WVS7中2%的数据,包含24个变量。 具体变量信息可通过?drhur::wvs7查看。

数据探索

数据探索指对陌生数据的数据构成、结构、形式、内容的初步了解,是数据分析的第一步,也是关键一步。

概览原始数据

wvs7
  • Tidy data(tibble
    • 行:观测单元
    • 列:变量
    • 单元:数值

了解数据结构

  • 观测量
  • 变量名及数量
  • 数据结构
wvs7
nrow(wvs7) # 获取数据的行数
ncol(wvs7) # 获取数据的列数
names(wvs7) # 获取变量名/列名
str(wvs7) # 获取变量名、变量名类型、行数、列数

变量提取

基于数据讨论变量特征,首先要了解如何表达数据和变量的从属关系。 包括R在内的OOP系统非常擅长多数据、多变量的协同使用和分析。 换言之,与一些常见数据分析软件不同,R可以同时加载和综合使用多个数据——只要将他们存入不同的对象即可。

OOP → 多数据分析 → 数据信息 + 变量信息

wvs7[, "country"]
wvs7$country

变量特征

变量信息提取与上一节中的向量信息提取完全一致。 我们也可以通过table命令获得变量分布,通过summary命令获得常见变量信息。 当然,R也支持获取年龄变量的总和、平均数、中位数、最小值、最大值、方差、IQR等,这些方法我们会在下一节仔细讨论。

table(wvs7$age)
summary(wvs7$age)

对于非数值型变量,我们可以通过总结表的形式获取他们的信息

table(wvs7$female)
## 
## FALSE  TRUE 
##   588   676
table(wvs7$marital)
## 
##    Married Cohabitant   Divoiced  Separated    Widowed     Single 
##        698         82         55         35         78        306

对于基于factor的变量,我们还能提取他们的层级信息

levels(wvs7$religious)
## [1] "Religious"     "Non-religious" "Atheist"
levels(wvs7$marital)
## [1] "Married"    "Cohabitant" "Divoiced"   "Separated"  "Widowed"    "Single"

变量属性

变量可能由于其类不同而具有不同的特征,比如定类向量就没法求平均值,因此mean对于他们就是无意义的。 但所有变量都具有一些属性特征,比如变量的长度、类别、特征值等。 而对这些特征的提取命令也是共通的。

length(wvs7$age) #求取年份的长度(此处为行数)
## [1] 1264
unique(wvs7$age)
##  [1] 66 34 47 51 45 61 50 48 52 42 53 33 49 57 63 68 56 19 35 28 38 67 24 30 62 79 72 37
## [29] 54 75 55 60 78 64 41 23 31 69 83 18 70 58 25 40 26 36 39 32 20 22 21 73 44 59 17 27
## [57] 46 29 65 77 43 82 85 74 NA 81 71 76 94
summary(wvs7$age) #获取年份的上述所有信息
##    Min. 1st Qu.  Median    Mean 3rd Qu.    Max.    NA's 
##   17.00   28.00   40.00   41.93   54.00   94.00       6
class(wvs7$age) #查看年份结构:vector、matrix、array、dataframe、list
## [1] "numeric"
typeof(wvs7$age) #查看年份元素类型
## [1] "double"

变量总览

summary(wvs7$age)
##    Min. 1st Qu.  Median    Mean 3rd Qu.    Max.    NA's 
##   17.00   28.00   40.00   41.93   54.00   94.00       6
summary(wvs7)
##    country            female             age          education             religious  
##  Length:1264        Mode :logical   Min.   :17.00   Min.   :0.000   Religious    :842  
##  Class :character   FALSE:588       1st Qu.:28.00   1st Qu.:2.000   Non-religious:310  
##  Mode  :character   TRUE :676       Median :40.00   Median :3.000   Atheist      : 79  
##                                     Mean   :41.93   Mean   :3.394   NA's         : 33  
##                                     3rd Qu.:54.00   3rd Qu.:5.000                      
##                                     Max.   :94.00   Max.   :8.000                      
##                                     NA's   :6       NA's   :40                         
##        marital     incomeLevel     equalIncentive    equalFreedom   corruption_state
##  Married   :698   Min.   : 1.000   Min.   : 1.000   Min.   :1.000   None: 74        
##  Cohabitant: 82   1st Qu.: 3.000   1st Qu.: 4.000   1st Qu.:1.000   Few :476        
##  Divoiced  : 55   Median : 5.000   Median : 7.000   Median :1.000   Most:427        
##  Separated : 35   Mean   : 4.697   Mean   : 6.284   Mean   :1.433   All :178        
##  Widowed   : 78   3rd Qu.: 6.000   3rd Qu.: 9.000   3rd Qu.:2.000   NA's:109        
##  Single    :306   Max.   :10.000   Max.   :10.000   Max.   :2.000                   
##  NA's      : 10   NA's   :39       NA's   :23       NA's   :23                      
##  corruption_local corruption_civil  trust_family  trust_neighbor  trust_stranger 
##  None: 78         None: 92         Min.   :1.00   Min.   :1.000   Min.   :1.000  
##  Few :531         Few :558         1st Qu.:4.00   1st Qu.:2.000   1st Qu.:1.000  
##  Most:392         Most:365         Median :4.00   Median :3.000   Median :2.000  
##  All :144         All :132         Mean   :3.72   Mean   :2.803   Mean   :1.896  
##  NA's:119         NA's:117         3rd Qu.:4.00   3rd Qu.:3.000   3rd Qu.:2.000  
##                                    Max.   :4.00   Max.   :4.000   Max.   :4.000  
##                                    NA's   :4      NA's   :16      NA's   :28     
##  trust_foreigner confidence_armedForce confidence_policy confidence_court
##  Min.   :1.000   Min.   :1.000         Min.   :1.000     Min.   :1.000   
##  1st Qu.:1.000   1st Qu.:2.000         1st Qu.:2.000     1st Qu.:2.000   
##  Median :2.000   Median :3.000         Median :3.000     Median :3.000   
##  Mean   :2.125   Mean   :2.862         Mean   :2.611     Mean   :2.527   
##  3rd Qu.:3.000   3rd Qu.:4.000         3rd Qu.:3.000     3rd Qu.:3.000   
##  Max.   :4.000   Max.   :4.000         Max.   :4.000     Max.   :4.000   
##  NA's   :54      NA's   :69            NA's   :45        NA's   :66      
##  confidence_gov  confidence_parliament confidence_civil confidence_parties
##  Min.   :1.000   Min.   :1.000         Min.   :1.000    Min.   :1.000     
##  1st Qu.:1.000   1st Qu.:1.000         1st Qu.:2.000    1st Qu.:1.000     
##  Median :2.000   Median :2.000         Median :2.000    Median :2.000     
##  Mean   :2.342   Mean   :2.164         Mean   :2.376    Mean   :2.022     
##  3rd Qu.:3.000   3rd Qu.:3.000         3rd Qu.:3.000    3rd Qu.:3.000     
##  Max.   :4.000   Max.   :4.000         Max.   :4.000    Max.   :4.000     
##  NA's   :61      NA's   :64            NA's   :55       NA's   :60        
##  confidence_tv  
##  Min.   :1.000  
##  1st Qu.:2.000  
##  Median :2.000  
##  Mean   :2.372  
##  3rd Qu.:3.000  
##  Max.   :4.000  
##  NA's   :26

数据梳理

如果说数据探索是从数据中看变量,那么数据梳理就是以变量为索引来了解数据。 从实用角度出发,我们这里直接介绍如何使用tidyverse进行数据梳理。 但其实绝大部分数据梳理都是可以通过R的自带语句完成的。 我们也将自带语句对于同一任务的操作放置在“提示”单元中。

首先介绍一下tidyverse

  • 是一个R包
  • 是一群R包
  • 像漫威和DC漫画宇宙一样,所有tidyverse组成成员都在同一个数据结构内工作,可以相互对话,共同使用。

安装

install.packages("tidyverse")
library("tidyverse")

dplyr

tidyverse中专门负责数据清理的组件,贯彻一个函数做一件事的风格。

通道

在逐一介绍dplyr的主要命令前,我们要先说明一下“通道”(pipe)。 通道在R中可以直接使用|>表示,在叫起dplyr后还可以使用功能更强的%>%。 我们后面的例子直接使用后者。 在R中,通道起到连接对于同一个对象的连续动作,相当于动作游戏中“搓”一个连续技。

另一方面,通道也能让每个命令排布更加明确、易读。 再举一个例子,如果我们用代码来模拟煮饺子的全过程,大体是这样:

eat_dumpling <- 
eat(
  dip(
    cook(
      fill(
        mix(
          meat, 
          with = c(salt, soy_sauce, green_onion, ginger)
          ), 
        with = wrapper
        ), 
      in = boilled_water
      ), 
    in = vinegar)
  )

使用通道后,可以写成这样

eat_dumpling <- 
mix(meat, with = c(salt, soy_sauce, green_onion, ginger)) %>% 
  fill(with = wrapper) %>% 
  cook(in = boilled_water) %>% 
  dip(in = vinegar) %>% 
  eat

%>%的快捷键:

  • Ctrl + Shift + M (Win)
  • Cmd + Shift + M (Mac)

变量筛选

select(<data>, <var1>, <var2>, ...)

现在数据中有24个变量,有一些有意思的变量排在后面不方便看到,我们希望看到一个只有国家、年龄、教育水平和对政府信心的数据框:

select(wvs7, country, age, education, confidence_gov)

# 如果我们想看到关于信心的所有变量,除了逐个列出来以外还能怎么做?
select(wvs7, country, age, education, starts_with("confidence"))

starts_with类似的还有ends_withmatches

提示:注意第三人称变单数😝

删除变量可以通过-实现:

select(wvs7, -(country:education))

一个select衍生体是rename,语法为new.name = old.name

rename(wvs7, nationality = country)

数据排序

arrange(<data>,...)

比如我们好奇最年轻的人群对国家机关的信心,并对应他们的教育水平、收入水平等信息。

select(wvs7, age, confidence_gov, education, incomeLevel)
select(wvs7, age, confidence_gov, education, incomeLevel) %>% 
  arrange(age)

那最年长的那群人呢?

select(wvs7, age, confidence_gov, education, incomeLevel) %>% 
  arrange(desc(age))

## 如果我们想知道最年轻又教育最高的人呢?
select(wvs7, age, confidence_gov, education, incomeLevel) %>% 
  arrange(age, desc(education))

变量值筛选

前面提到select是对数据库变量的筛选,filter则基于变量值的筛选。 延续上面的例子,如果我们好奇美国最年轻的一群人对国家机关的信心以及他们的教育水平和收入水平。

select(wvs7, age, confidence_gov, education, incomeLevel) %>% 
  arrange(age)
select(wvs7, age, confidence_gov, education, incomeLevel, country) %>% 
  filter(country == "United States") %>% 
  arrange(age)

select(wvs7, age, confidence_gov, education, incomeLevel, country) %>% 
  filter(country == "United States") %>% 
  filter(age == min(age, na.rm = TRUE))

数据修改

在数据分析中,我们常常要将数据进行调整和再加工,mutate可以帮你做到这一点。 英文中“mutate”表示“变异”,也就是说这个函数可以实现的并不是无中生有,而是改头换面。

如果我们关心教育水平对收入水平差异的影响,我们可以建立一个比例变量

mutate(wvs7, ratio_incomeEdu = incomeLevel / (education + 1)) %>%
  select(country, incomeLevel, education, ratio_incomeEdu) %>%
  arrange(desc(ratio_incomeEdu))

# 如果要把`ratio_incomeEdu`变成一个百分数,怎么办?
mutate(wvs7, 
       ratio_incomeEdu = incomeLevel / (education + 1),
       ratio_incomeEdu = as.numeric(ratio_incomeEdu) %>%
         scales::percent()) %>%
  select(country, ratio_incomeEdu)

dplyr还允许批量修改

如何对变量进行批量修改?dplyr和它的朋友tidyselect提供了acrosswhere函数。 比如出于某种需要,我们要给每个数字变量都取对数。

流程: 判断 → 筛选 → 修改

wvs7 %>%
  mutate(across(where(is.numeric), log))

# 是否能使用这套技巧将所有人际信任变量都倒叙?
wvs7 %>% 
  mutate(across(starts_with("trust"), ~ 5 - .))

数值统计

count用来基于数据计数。 比如,可以使用count来计算我们的数据中男性、女性各有多少人。1

wvs7 %>%
  count(female)

# 如果想知道不同年龄段的男女数量怎么办呢?
wvs7 %>%
  count(age, female)

  1. 这种列表在人口普查等统计数据中非常常见。↩︎

summarise 用来将个体数据转换成统计数据。 比如,我们想获得样本的年龄和教育水平平均数

wvs7 %>%
  summarise(age = mean(age, na.rm = TRUE),
            edu = mean(education, na.rm = TRUE))

我们前面提到的across, start_with等同样可以在这里使用: 比如我们想获得所有关于机构信心变量的平均值:

wvs7 %>%
  summarise(across(starts_with("confidence"), mean, na.rm = TRUE))

group_by使分组操作成为可能:

wvs7 %>%
  group_by(female) %>% 
  summarise(age = mean(age, na.rm = TRUE),
            edu = mean(education, na.rm = TRUE))

group_by实际是为现有数据建立群体索引,之后的所有操作都将在分组进行。 这一命令的逆操作是ungroup

Bonus

怎么样才能填补缺失的 x, 然后把 yz 合并成一个变量呢?

df_toy <- data.frame(x = sample(c(1:2, NA, NA, NA)),
                     y = c(1, 2, NA, NA, 5),
                     z = c(NA, NA, 3, 4, 5))

df_toy
df_toy %>%
  mutate(x = coalesce(x, 0L),
         yz = coalesce(y, z)) # Ta-da~~~

数据整理原则

我们上面的一系列操作都有一个共同的特点,你发现了吗?

head(wvs7)
  • 不碰原始数据
  • 对象覆盖需谨慎
wvs7 <- mutate(wvs7, female = as.numeric(female))

数据整合

直接合并

直接合并的前提基本和矩阵运算是基本一致的:只有前列数对得上后行才能进行。

  • 行合并: 列数相等(并不是~)
  • 列合并: 行数相等

分别举个例子:

wvs7_us <- filter(wvs7, country == "Unitied States")
wvs7_russia <- filter(wvs7, country == "Russia")

# 创建一个美俄数据

bind_rows(wvs7_us, wvs7_russia)

# 不等列行合并会发生什么?
# Try this
bind_rows(tibble(x = 1:3), tibble(y = 1:4))
wvs7_conf <- select(wvs7, starts_with("confidence"))
wvs7_trust <- select(wvs7, starts_with("trust"))

# 创建一个信心-信任数据

bind_cols(wvs7_conf, wvs7_trust)

索引合并

索引合并指的是基于共享的索引序列(可以是任何变量)合并数据。

让我们先创建两个演示数据:

  1. 个体层级平等认知数据;
  2. 国家层级人口变量数据。

如果wvs7_eq是调查数据,wvs7_country是人口统计数据,我们的研究需要将这两组数据合并分析个体层级和国家层级变量间的关系。

wvs7_eq <- select(wvs7, country, starts_with("equal")) %>% 
  filter(country %in% unique(country)[1:2])

wvs7_country <- group_by(wvs7, country) %>% 
  summarise(across(female:education, mean, na.rm = TRUE)) %>% 
  ungroup %>% 
  filter(country %in% unique(country)[2:3])
inner_join(wvs7_eq, wvs7_country)
left_join(wvs7_eq, wvs7_country)
right_join(wvs7_eq, wvs7_country)
full_join(wvs7_eq, wvs7_country)

总结

  1. 行动之前 想清楚;
  2. 巧妙且综合地使用 dplyr 函数;
    • 探索: glimpse, head, tail
      • 结构:nrow, ncol, names, str
      • 特征:table, levels
      • 属性:length, unique, summary, class, typeof
    • 梳理: dplyr命令集
  3. 数据整合原则:不碰原数据
  4. 数据合并
    • 直接合并:bind_*
    • 索引合并:*_join

数据整理

Learning R with Dr. Hu and His Friends

胡悦(清华大学政治学系)