data.frame中的字符串变换矢量元素

时间:2012-04-10 14:50:29

标签: r dataframe

我有一个庞大的数据框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(与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中的元素吗?

3 个答案:

答案 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在分解代码(快速解决方案中最慢的部分)上获得更好的属性。