写在前面
为什么要用R进行数据清理?
我大概能想到的:
- 能方便、快捷地处理大样本数据(几小时甚至几分钟内完成数天的工作);
- 方便对数据清理过程进行double check(数据清理过程有什么缺漏,可以直接通过看代码文件、跑代码来找出来,修改起来也很方便)以及修改(例如,你想要对另一个dataset进行类似的操作,只需要copy代码然后修改一些内容,无需进行重复的操作);
- 如果我们知道怎么用R清理数据,那么肯定对R以及数据的内容有了一定的了解,接下来用R进行数据分析也会驾轻就熟。
当然,写代码也有缺陷,某些功能的实现其实不如Excel或者SPSS轻松,所以其实不必万事都指望用R来完成。我个人一直推崇“什么方便用什么”,学R就是为了图方便,不要拘泥于一定要全部的数据分析都通过R来完成。
关于本文
我没有经过专业的R语言的训练(例如,没有接触过这方面的workshop或课程),基本上都是边学边做(例如自己从网上找来的或自己琢磨出来的),如果大家有更方便的方法,欢迎交流、分享。
本文假定大家已经有了一定的R基础。
主要针对心理学领域的数据的清理,不过或许也适用于其他研究领域的数据。
数据导入
一些R语言教学书籍或者使用R进行数据分析的文献的supplemental materials会提供数据导入的R代码。但有个问题是,他们使用的R包(package)可能比较旧或者不适用于我们自己的数据格式。
因此,无论是对于新手,还是对于熟悉R代码的老手,个人觉得最稳妥的方式都是通过RStudio的Import Dataset
按钮进行数据导入。见下图,如果是txt或者csv格式的数据,可以选择From Text
的选项,另外可以导入Excel、SPSS、SAS、Stata的数据。
以Excel格式的数据为例。我们可以通过Data Preview区域查看数据是否正确(例如header是否作为变量名),同时在Code Preview区域还会显示对应的R包和代码,个人推荐大家将这些代码复制到R文件中,这样就不需要每次都点击Import Dataset
按钮进行导入,直接跑代码就好。
标注缺省值的方法
可以在导入数据时指定某个特定的值(例如-3)作为缺省值。如果没有在数据导入时这么做,也可以通过以下代码实现。
data[data == -3] = NA
去掉某一个column的方法
如下。
data$itemX <- NULL
计算某个量表的总分或均分
例如某个量表有三道题目(Q01
、Q02
、Q03
),我们想计算每个被试在这个量表的总分/均分,并记录到QuestionnaireX
这一列中。
计算总分:
data$QuestionnaireX <- data$Q01 + data$Q02 + data$Q03
计算均分:
data$QuestionnaireX <- (data$Q01 + data$Q02 + data$Q03)/3
以上两个都是笨方法,但好处是一看便会用。如果题目较多,计算总分可以用rowSums
函数,计算均值可以用rowMeans
函数。
反向计分
可以通过psych
包的reverse.code
函数来实现。
例如某个5点量表有三道题目(Q01
、Q02
、Q03
),其中第二道题应当反向计分。因为是5点量表,所以最小值为1,最大值为5。
代码如下(先取subset,再进行操作)。
data$QuestionnaireX <- subset(data, select=c(Q01, Q02, Q03))
data$QuestionnaireX_new <- reverse.code(keys=c(1,-1,1), data$QuestionnaireX, mini=1, maxi=5)
说明:参数keys
用于指定需要反向计分的题目,1表示正向计分,-1表示反向计分。参数mini
和maxi
是指定计分的范围。
对变量进行重新编码其一:cut函数
如下。这个方法也可以用于对测谎题进行编码,并进而依据作答情况筛选被试。
data$newX <- cut(data$itemX,
breaks=c(0, 3, 6, 9),
labels=c("low", "medium", "high"))
说明:在这个例子中,我们根据data$itemX
的值进行编码,breaks
和labels
的意思就是,如果data$itemX
这一列的某个单元格的值在(0, 3],则编码为“low”,如果在(3, 6],则编码为“medium”,如果在(6, 9],则编码为“high”。编码的结果赋值到data$newX
这一列中。
对变量进行重新编码其二:mutate函数
来自dplyr
包的mutate
函数可以将数值编码为二分变量。
例如,某道单选题itemX
的选项有1、2、3、4、5,其中1是正确答案,我们想将这道题的值重新编码为1和0,1是回答正确,0是回答错误。代码如下。
data <- data %>% mutate(newX = case_when(itemX == 1 ~ 1, TRUE ~ 0))
说明:这里主要通过一个简单的逻辑判断(if……else……)来对题目进行编码。如果data$itemX
的值为1,则编码为1,否则编码为0(TRUE在这里的意思相当于else)。编码的结果赋值到data$newX
这一列中。
根据测谎题的情况筛选被试
这里提供我的思路,欢迎交流。
思路一:
data <- data[data$itemLIE1 + data$itemLIE2 + data$itemLIE3 + data$itemLIE4 == 4,]
说明:先将所有测谎题编码为二分变量(1=回答正确,0=错误),然后通过一个简单的逻辑判断来筛选特定的row,例如这里有4道测谎题,所以我筛选了所有四道题都回答正确的被试的row。
也可以设置其他判断要求,例如“>=3”代表筛选回答正确三道题目以上的被试;也可以设置为正确率。
思路2:
如何只保留全部回答正确的被试,也可以用subset
函数。
data <- subset(data, itemLIE1 == 1 & itemLIE2 == 1 & itemLIE3 == 1 & itemLIE4 == 1)
注意这里也需要先将测谎题编码为二分变量(1=回答正确,0=错误)。
根据某个特定的题目筛选被试
思路一:我们想找出itemX的回答不为1的被试。可以用subset
函数:
data <- subset(data, data$itemX != 1)
思路2:我们想找出itemX的回答为2、3或4的被试。可以用dplyr
包的filter
函数:
data <- filter(data, itemX %in% c(2, 3, 4))
分组
例如,itemX为分类变量,我们想将其作为分组变了,则可以:
data$groupX <- as.factor(data$groupX)
这样做的好处是你可以立刻通过summary
函数来查看每组的人数。
匹配两个dataset的被试
例如,你在做一个纵向研究/追踪研究/队列研究,现在手上有两个time point的数据dataT1
、dataT2
。你希望根据某个独一无二的编号(可能是身份证号、学生/工作证号、被试编号,这里我们称为subID
),找出同时完成了两次调查的被试,以便进行后续的分析。
代码如下。
dataT1_matched <- dataT1[dataT1$subID %in% dataT2$subID,]
dataT2_matched <- dataT2[dataT2$subID %in% dataT1$subID,]
筛选后每一行的顺序不一定一一对应(例如某个被试在T1可能在第1行,但在T2可能在第19行),可以通过sort
函数基于subID
重新排个序。
简单的描述统计其一:快速查看符合特定条件的被试的数量
用nrow
函数,代码如下。
nrow(data[data$condition1 == "0" & data$condition2 == "0",])
说明:查看data
中condition1的值为0且condition2的值也为0的被试的数量。
简单的描述统计其二:连续变量和分类变量的描述统计
可以用一些函数来快速查看你感兴趣的变量的描述统计结果。连续变量用psych
包的describe
,分类变量用summarytools
包的freq
函数。另外stargazer
等第三方包可以生成很漂亮的描述统计表。具体就不展开了。
数据导出
推荐导出为csv格式的文件。
可以使用tidyverse
包的write_excel_csv2()
函数来避免中文字符乱码的问题(该函数默认的编码格式为utf-8)。
write_excel_csv(x=data, file="/Users/weiziqian/Desktop/data_test.csv")
注:数据导入、导出过程中的过程中可能出现中文字符乱码的问题,解决问题的关键在于使用正确的函数,并且参数encoding='utf-8'。
----------2022.11.09更新----------
添加了反向计分