【问题标题】:Using ifelse with transform in ddply在 ddply 中使用 ifelse 和 transform
【发布时间】:2013-03-01 18:48:09
【问题描述】:

我正在尝试使用 ddplytransform 在数据框中使用变量 IDDate 填充新变量 (summary_Date)。变量的值是根据使用ifelse 评估的片段的长度选择的:

如果在给定月份中对 ID 的观察少于五个,我希望通过将日期四舍五入到最接近的月份来计算 summary_Date(使用包 lubridate 中的 round_date);如果在给定月份中对某个 ID 的观察超过五个,我希望 summary_Date 简单地为 Date

require(plyr)
require(lubridate)

test.df <- structure(
  list(ID = c(1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,1, 1, 1, 1, 1, 1, 1, 1, 1
                , 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,2, 2, 2, 2, 2, 2, 2, 2)
       , Date = structure(c(-247320000, -246196800, -245073600, -243864000
                            , -242654400, -241444800, -126273600, -123595200
                            , -121176000, -118497600, 1359385200, 1359388800
                            , 1359392400, 1359396000, 1359399600, 1359403200
                            , 1359406800, 1359410400, 1359414000, 1359417600
                            , 55598400, 56116800, 58881600, 62078400, 64756800
                            , 67348800, 69854400, 72964800, 76161600, 79012800
                            , 1358589600, 1358676000, 1358762400, 1358848800
                            , 1358935200, 1359021600, 1359108000, 1359194400
                            , 1359280800, 1359367200), tzone = "GMT"
                          , class = c("POSIXct", "POSIXt"))
       , Val=rnorm(40))
  , .Names = c("ID", "Date", "Val"), row.names = c(NA, 40L)
  , class = "data.frame")

test.df <- ddply(test.df, .(ID, floor_date(Date, "month")), transform
                 , summary_Date=as.POSIXct(ifelse(length(ID)<5
                                                  , round_date(Date, "month")
                                                  ,Date)
                                           , origin="1970-01-01 00:00.00"
                                           , tz="GMT")
                 # Included length_x to easily see the length of the subset
                 , length_x = length(ID))

head(test.df,5)
#   floor_date(Date, "month") ID                Date        Val summary_Date length_x
# 1                1962-03-01  1 1962-03-01 12:00:00 -0.1037988   1962-03-01        3
# 2                1962-03-01  1 1962-03-14 12:00:00  0.2923056   1962-03-01        3
# 3                1962-03-01  1 1962-03-27 12:00:00  0.4435410   1962-03-01        3
# 4                1962-04-01  1 1962-04-10 12:00:00  0.1159164   1962-04-01        2
# 5                1962-04-01  1 1962-04-24 12:00:00  2.9824075   1962-04-01        2

ifelse 语句似乎正在工作,但“summary_Date”中的值似乎是为转换正在处理的子集计算的第一个值,而不是特定于行的值。例如在第 3 行中,summary_Date 应该是 1962-04-01,因为日期 1962-03-27 12:00:00' 应该向上取整(因为子集中的行少于五行),而是 summary_Date 的第一个计算值 (@987654338 @) 在该子集中的所有行中重复。

编辑:我受到 Ricardo 使用 data.table 的回答的启发,尝试使用 ddply 分两步进行。它也有效:

test.df <- ddply(test.df, .(ID, floor_date(Date, "month")), transform
                 , length_x = length(ID))

test.df <- ddply(test.df, .(ID, floor_date(Date, "month")), transform
                 , summary_Date=as.POSIXct(ifelse(length_x<5
                                                  , round_date(Date, "month")
                                                  ,Date)
                                           , origin="1970-01-01 00:00.00"
                                           , tz="GMT"))

head(test.df,5)[c(1,3:7)]
#   floor_date(Date, "month") ID                Date        Val length_x summary_Date
# 1                1962-03-01  1 1962-03-01 12:00:00 -0.1711212        3   1962-03-01
# 2                1962-03-01  1 1962-03-14 12:00:00 -0.1531571        3   1962-03-01
# 3                1962-03-01  1 1962-03-27 12:00:00  0.1256238        3   1962-04-01
# 4                1962-04-01  1 1962-04-10 12:00:00  1.4481225        2   1962-04-01
# 5                1962-04-01  1 1962-04-24 12:00:00 -0.6508731        2   1962-05-01

【问题讨论】:

  • 嗨@AndyT,我有点困惑:你说第4行summary_Date应该是1962-04-01——这是你在输出中的值。我没有看到错误是什么?
  • 哎呀——那是我的错误。我的意思是第 3 行。1962-03-27 应该四舍五入到1962-04-01,因为子集中的行少于 5 行。我已经编辑过了。
  • 你能详细说明你在数什么吗?是每个给定月份的 ID 数量吗?
  • 给定ID在给定月份的ID数;即,给定 ID 和月份的子集中向量 ID 的长度(我可以选择任何数据框列)。通常我会使用nrow(x),其中x 是在ddply 的每次迭代中transform 正在处理的子集,但我没有要传递给nrow 的数据框名称。
  • 使用mutate而不是transform,您可以将两个ddply调用合二为一。区别在于mutate 尊重新创建的列,而transform 不尊重。

标签: r plyr lubridate


【解决方案1】:

一步ddply解决方案(也作为评论发布)

ddply(test.df, .(ID, floor_date(Date, "month")), mutate, 
  length_x = length(ID), 
  summary_Date=as.POSIXct(ifelse(length_x < 5, round_date(Date, "month") ,Date)
    , origin="1970-01-01 00:00.00", tz="GMT")
)

【讨论】:

  • 太棒了,感谢 Ramnath。我使用 transform 进行了尝试,显然得到了length_x not found 的错误。我不知道变异,所以谢谢!
  • 我还在mutate 的帮助中看到它比transform 快 - 对我来说很好,因为我正在处理的数据框相当大,而且这个操作很慢transform.
  • 对于大型数据帧,我会选择data.table,因为它通常比plyr 更快。
【解决方案2】:
# transform to data.table
library(data.table)
test.dt <- data.table(test.df)

# calculate length of id by month-year. 
test.dt[, idlen := length(ID),  by=list(month(Date), year(Date)) ]

# calculate the summary date
test.dt[, summary_Date := ifelse(idlen<5, as.Date(round_date(Date, "month")), as.Date(Date))]

# If you would like to have it formatted add the following: 
test.dt[, summary_Date := as.Date(summary_Date, origin="1970-01-01")]

结果:

 > test.dt
    ID                Date         Val idlen summary_Date
 1:  1 1962-03-01 12:00:00  0.42646422     3   1962-03-01
 2:  1 1962-03-14 12:00:00 -0.29507148     3   1962-03-01
 3:  1 1962-03-27 12:00:00  0.89512566     3   1962-04-01   <~~~~~
 4:  1 1962-04-10 12:00:00  0.87813349     2   1962-04-01
 5:  1 1962-04-24 12:00:00  0.82158108     2   1962-05-01
 6:  1 1962-05-08 12:00:00  0.68864025     1   1962-05-01


更新:

解释为什么需要两个步骤

无法一步完成的原因与每个组只能获得一个值有关。当您将该值分配给组的成员时,您将 1 个元素分配给多个。 R 非常清楚如何处理这种情况:recycling 单个元素。

但是,在这种特殊情况下,您不想回收;相反,您不想将1 元素应用于many。因此,您需要独特的组,这是我们在第二步中所做的。然后,组的每个元素(行)都被分配了自己的特定值。

更新 2:

@Ramnath 给出了使用mutate 的好建议。看看?mutate,它给出了:

这个函数与 transform 非常相似,但它迭代地执行转换...... 后面的转换可以使用早期转换创建的列

这正是您想要做的!

【讨论】:

  • 这确实做到了 - 感谢里卡多。您的回答使我像使用 data.table 一样分两步尝试使用 ddply。它也可以这样工作 - 请在我的原始帖子中查看我的编辑。我仍然不确定为什么不能一步完成,但我现在会继续这样做。
猜你喜欢
  • 1970-01-01
  • 2013-11-03
  • 1970-01-01
  • 1970-01-01
  • 2017-02-09
  • 1970-01-01
  • 1970-01-01
  • 2022-08-20
  • 1970-01-01
相关资源
最近更新 更多