1

我有一个巨大的数据框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.Dateformat功能。但是由于这些值没有任何日期,因此它不适用于字符串。使用 Numerics(与数据框列相同)我什至遇到了其他问题。

> 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 中的元素的简单方法?

4

3 回答 3

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()

于 2012-04-10T15:05:41.040 回答
4

之所以

> as.Date("201001", format = "%Y%m")
 [1] NA

不起作用,是 R 日期需要一个日期组件。由于您的日期没有提供,因此您会得到一个缺失值。为了避免这种情况,只需添加一个 day 组件:

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"
于 2012-04-10T14:58:02.520 回答
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 倍。

PS如果你真的关心性能,你可以通过使用hash-backed factor获得更好的因式分解代码(快速解决方案中最慢的部分)的属性。

于 2012-04-10T15:11:28.117 回答