本篇分享一个处理技巧,也来自一位读者的咨询。示例数据如下所示(可在后台回复关键词示例数据获得)。
A2000-A2020五列为日期格式,其中包含缺失值;Year为某事件发生的年份。要求生成两列数据:
- 第一列为事件发生的具体日期,也就是变量Year中的年份所对应前五列中的日期。如第一行对应的就是A2015列中的日期,第二行对应的是A2020列中的日期;
- 第二列为发生日期之前的第一个非缺失日期。如第一行为A2010列的日期,第二行本应该是A2015列的日期,但是该列为缺失值,因此递补为A2010列的日期。
导入数据
test <- read.csv("104.缺失值test.csv",
stringsAsFactors = F,
na.strings = "",
fileEncoding = "UTF-8-BOM")
此步实际的参数功能如下:
stringsAsFactors = F
:默认状态下,read.csv()
函数会把字符变量当作是因子变量,该语句可避免这一情况;na.strings = ""
:将空字符串(即""
)当作是缺失值;fileEncoding = "UTF-8-BOM"
:防止列名乱码。- 读者可分别去掉相应语句比较数据导入的效果。
转换日期格式
将前五列转为日期格式:
library(magrittr)
library(tidyverse)
test %<>%
mutate(across(starts_with("A"),as.Date))
生成第一个变量
生成第一个变量的思路比较简单,只需要根据Year中的年份提取相应列中的数据即可。直接的方法是使用多层的if语句,不过dplyr
工具包提供了一个更快捷的函数case_when()
:
test %<>%
mutate(t1 = case_when(
Year == "2020"~A2020,
Year == "2015"~A2015,
Year == "2010"~A2010,
Year == "2005"~A2005,
))
生成第二个变量
相比之下,生成第二个变量的思路就不那么简单了,即使想使用if语句其逻辑也不是那么清晰。
小编的解决思路如下:
首先是把Year对应年份及其之后的日期全部转为缺失值。
为了不污染原始数据,这里使用一个临时数据框test0
:
test0 = test
test0$A2020 = NA
test0$A2015[test0$Year <= 2015] = NA
test0$A2010[test0$Year <= 2010] = NA
test0$A2005[test0$Year <= 2005] = NA
此时,test0
中各行最大的一个日期即为所求的第二个变量。
小编不清楚日期怎么比较大小,因此先将它们转为数值变量,方法同上:
test0 %<>%
mutate(across(starts_with("A"),as.numeric))
max()
函数可以用于求最大值:
test0 %<>%
mutate(t2 = max(A2015, A2010, A2005, A2000,
na.rm = T))
na.rm = T
:设置缺失值不参与计算。
可以发现,上面代码计算的变量所有行的数值都是一样的,因为它是针对所有的数值求最大值的,这与预期目标不符。为了能够按行求最大值,需要在其在其使用rowwise()
函数:
test0 %<>%
rowwise() %>%
mutate(t2 = max(A2015, A2010, A2005, A2000,
na.rm = T))
再把数值变量转为日期变量:
test$t2 <- as.Date(test0$t2, origin = "1970-1-1")
- R中默认1970年1月1日对应的数值为0。
需要示例数据请在后台回复关键词示例数据。