我有一个庞大的数据框df
,在一列中有一个'年 - 月'值,如下所示:“YYYYMM”。目前,数据类型是一个数字。快照:
> df[[1]][1:10]
[1] 201001 201001 201001 201001 201001 201001 201001 201001 201001 201001
> str(df)
'data.frame': 2982393 obs. of 11 variables:
$ YearMonth : int 201001 201001 201001 201001 201001 201001 201001 201001 201001 201001 ...
$ ...
我想要的是以“YYYY-MM”的形式将此值转换为字符串(最终为一个因子),以便能够将其与其他数据帧进行比较。
我正在努力寻找一种简单的方法来改变价值。
我尝试使用as.Date
和format
功能。但由于这些价值观没有任何日子,因此它对字符串不起作用。使用Numerics(与dataframe列相同),我甚至遇到了其他问题。
> as.Date("201001", format = "%Y%m")
[1] NA
> as.Date(201001, format = "%Y%m")
Error in as.Date.numeric(201001, format = "%Y%m") :
'origin' must be supplied
> as.Date(df[[1]], format = "%Y%m")
Error in as.Date.numeric(df[[1]], format = "%Y%m") :
'origin' must be supplied
我只能使用subset
和字符串连接来转换一个值。
我写了下面的公式来处理一个元素:
transformString <- function( x ) { # x = value
return ( paste(cbind(substring(x, 1, 4),"-",substring(x,5,6)), collapse = '') )
}
问题:我没有找到一种简单的方法将该函数应用于data.frame的整个列,而不仅仅是遍历所有元素:
transformStringVector <- function( x ) { # x = vector
for(i in 1:length(x)) {
x[i]<-transformString(x[i])
}
return ( x )
}
这远非优雅而且不利于表现。我尝试使用apply
(见下文)和类似的东西,但遇到了错误......(我承认我并没有真正得到apply
函数)
> temp <- apply(df[[1]], 1, transformString )
Error in apply(df[[1]], 1, transformString ) :
dim(X) must have a positive length
在巨大的data.frame中,有没有人可以替代这种转换?或者更一般;一种简单的方法,可以将类似字符串的转换应用于data.frame中的元素吗?
答案 0 :(得分:4)
原因
> as.Date("201001", format = "%Y%m")
[1] NA
不起作用,是R日期需要一天组件。由于您的日期没有提供,因此您将获得缺失值。要避免这种情况,只需添加一天组件:
R> x = c("201001","201102")
R> x = paste(x, "01", sep="")
所以我把所有日期都放在了这个月的第一天:
R> y = as.Date(x, "%Y%m%d")
[1] "2010-01-01" "2011-02-01"
然后,您可以使用format
来获得所需内容:
R> format(y, "%Y-%m")
[1] "2010-01" "2011-02"
答案 1 :(得分:4)
如果您只是想将列值转换为指定格式的字符串而不关心date
格式,substr()
和paste()
都采用向量作为参数:
xx<-c(201011,201003,201002,201010,201009,201005,201001,201001,201001,201001)
paste(substr(xx,1,4),substr(xx,5,6),sep="-")
# [1] "2010-11" "2010-03" "2010-02" "2010-10" "2010-09" "2010-05" "2010-01"
# [8] "2010-01" "2010-01" "2010-01"
通过这种方式,您不必使用apply()
答案 2 :(得分:2)
要回答有关将此应用于data.frame的问题,您可以使用$
运算符访问该列。所以你可以使用这里提供的任何一个函数(我会使用substr变量)来完成它。如果你打算转换成一个因素,我会先做。
> df <- data.frame(a=1:5,b=5:1,d=200101:200105)
> df
a b d
1 1 5 200101
2 2 4 200102
3 3 3 200103
4 4 2 200104
5 5 1 200105
> #Convert to a factor now for performance reasons.
> df$d <- as.factor(df$d)
> df$d <- paste(substr(df$d, 1, 4), "-", substr(df$d, 5,6), sep="")
> df
a b d
1 1 5 2001-01
2 2 4 2001-02
3 3 3 2001-03
4 4 2 2001-04
5 5 1 2001-05
> typeof(df$d)
[1] "character"
> df$d <- as.factor(df$d)
> df
a b d
1 1 5 2001-01
2 2 4 2001-02
3 3 3 2001-03
4 4 2 2001-04
5 5 1 2001-05
> typeof(df$d)
[1] "integer"
请注意,根据您的data.frame的“巨大”程度,您可以通过首先转换为因子,然后将级别转换为连字日期来获得更好的性能。
> df <- data.frame(a=rep(1:5,1000000),b=rep(5:1,1000000),d=rep(200101:200105, 1000000))
> nrow(df)
[1] 5000000
> # Hyphenate first
> system.time(df$d <- paste(substr(df$d, 1, 4), "-", substr(df$d, 5,6), sep="")) + system.time(df$d <- as.factor(df$d))
user system elapsed
9.65 0.61 10.31
>
> #Factor first
> system.time(df$d <- as.factor(df$d)) + system.time(levels(df$d) <- paste(substr(levels(df$d), 1, 4), "-", substr(levels(df$d), 5,6), sep=""))
user system elapsed
0.68 0.25 0.93
因此,根据data.frame的属性,您可以通过首先进行因子分解来提高性能10倍。
P.S。如果真的关心性能,您可以使用hash-backed factor在分解代码(快速解决方案中最慢的部分)上获得更好的属性。