我正在尝试使用每年的时间和类型构建堆积条。 我的数据库mat(head)看起来像
head(mat)
year flights.type flights.duration
1 2000 HR20 01:12:00
2 2000 HR20 02:00:00
3 2000 L4 00:54:00
4 2000 L4 00:42:00
5 2000 L4 00:22:00
6 2000 HR20 00:24:00
我想按年份和类型对flight.duration求和,然后构建一个堆积条。
我尝试使用函数聚合但是随着时间的推移,它无法正常工作。谁能帮我?按年份和类型划分的总和如下:
aggregate(mat$flights.duration,format(.POSIXct(mat$flights.duration,tz="GMT"), "%H:%M:%S"),FUN=sum, by=list(mat$year))
答案 0 :(得分:2)
使用data.table
包和as.difftime()
函数的解决方案:
library(data.table)
setDT(mat)[, .(flights.duration.minutes = sum(as.difftime(as.character(flights.duration)))),
.(year, flights.type)]
year flights.type flights.duration.minutes
1: 2000 HR20 216 mins
2: 2000 L4 118 mins
答案 1 :(得分:1)
您可以将Collective\Html\HtmlServiceProvider::class,
列转换为数字分钟值,如下所示:
'Collective\Html\HtmlServiceProvider',
然后,使用分组功能,例如来自flights.duration
包的功能,如下所示:
df$flights.duration <- apply(df, 1, function(x) {
sum(as.numeric(unlist(strsplit(x[3], ':'))) * c(60, 1, 0))
})
输出如下:
dplyr
编辑:使用library(dplyr)
df %>% group_by(year, flights.type) %>% summarise(flights.duration = sum(flights.duration))
软件包Source: local data frame [2 x 3]
Groups: year [?]
year flights.type flights.duration
<int> <chr> <dbl>
1 2000 HR20 216
2 2000 L4 118
而不是上面的tidyr
函数添加可能更快的其他选项行:
separate
结果与之前相同:
apply
答案 2 :(得分:0)
lubridate
包被广泛认为是R中可用的最佳日期/时间包。它基于R Date
和POSIXct
基类型,并添加了自己的Interval
包1}},Duration
和Period
类型。
普通hh:mm:ss
次最合适的数据类型是Period
类型。从理论上讲,应该可以将字符串时间解析为Period
值,然后使用sum()
执行直接分组aggregate()
。
不幸的是,事实证明这是一项比人们希望的要困难得多的任务。我最终得到了它,但它需要一些扭曲。
首先,这里是如何将字符串时间解析为Period
值。 lubridate
提供了一种方便的hms()
方法来执行此操作:
df <- data.frame(year=c(2000L,2000L,2000L,2000L,2000L,2000L),flights.type=c('HR20','HR20','L4','L4','L4','HR20'),flights.duration=c('01:12:00','02:00:00','00:54:00','00:42:00','00:22:00','00:24:00'),stringsAsFactors=F);
library(lubridate);
df$flights.duration <- hms(df$flights.duration);
df;
## year flights.type flights.duration
## 1 2000 HR20 1H 12M 0S
## 2 2000 HR20 2H 0M 0S
## 3 2000 L4 54M 0S
## 4 2000 L4 42M 0S
## 5 2000 L4 22M 0S
## 6 2000 HR20 24M 0S
其次,遗憾的是,lubridate
似乎没有为sum()
类型提供Period
方法:
sum(df$flights.duration);
## [1] 0
(如果你想知道它返回零的原因,Period
类型是通过将秒字段存储为矢量的有效载荷(双重类型)和剩余字段(分钟数)来实现的。 ,小时,天,月,年)存储为插槽,也是双重类型。df$flights.duration
中的所有值都有零秒,基本sum()
函数只能看到矢量有效负载,所以它会求和为零。)
我尝试使用S3方法自己填补这个空白,但很快发现它不起作用,因为Period
类型是S4类型。所以我写了这个S4方法:
setMethod('sum',signature(x='Period',na.rm='logical'),function(x,na.rm=FALSE) period(seconds=sum(as.double(x),na.rm=na.rm),minutes=sum(x@minute,na.rm=na.rm),hours=sum(x@hour,na.rm=na.rm),days=sum(x@day,na.rm=na.rm),months=sum(x@month,na.rm=na.rm),years=sum(x@year,na.rm=na.rm)));
## [1] "sum"
sum(df$flights.duration);
## [1] "3H 154M 0S"
不幸的是,还有一个问题:aggregate()
默认尝试简化聚合结果,这会将S4结果展平为非S4对象,丢失插槽并破坏数据:< / p>
res <- aggregate(flights.duration~year+flights.type,df,sum);
res;
## Error in paste(x@year, "y ", x@month, "m ", x@day, "d ", x@hour, "H ", :
## trying to get slot "year" from an object (class "Period") that is not an S4 object
traceback();
## 8: paste(x@year, "y ", x@month, "m ", x@day, "d ", x@hour, "H ",
## x@minute, "M ", x@.Data, "S", sep = "")
## 7: format.Period(x[[i]], ..., justify = justify)
## 6: format(x[[i]], ..., justify = justify)
## 5: format.data.frame(x, digits = digits, na.encode = FALSE)
## 4: as.matrix(format.data.frame(x, digits = digits, na.encode = FALSE))
## 3: print.data.frame(list(year = c(2000L, 2000L), flights.type = c("HR20",
## "L4"), flights.duration = c(0, 0)))
## 2: print(list(year = c(2000L, 2000L), flights.type = c("HR20", "L4"
## ), flights.duration = c(0, 0)))
## 1: print(list(year = c(2000L, 2000L), flights.type = c("HR20", "L4"
## ), flights.duration = c(0, 0)))
res$flights.duration;
## [1] 0 0
## attr(,"class")
## [1] "Period"
## attr(,"class")attr(,"package")
## [1] "lubridate"
isS4(res$flights.duration);
## [1] FALSE
如您所见,aggregate()
调用成功,但对象已损坏。 print.data.frame()
方法在列上失败,因为它恰好在其上调用format()
,它调度到S3方法format.Period()
,这是lubridate
命名空间下的私有方法。它在损坏的对象上失败。
我们可以防止简化:
res <- aggregate(flights.duration~year+flights.type,df,sum,simplify=F);
res;
## year flights.type flights.duration
## 1 2000 HR20 0
## 2 2000 L4 0
res$flights.duration;
## $`1`
## [1] "3H 36M 0S"
##
## $`4`
## [1] "118M 0S"
##
从技术上讲,它有效,但该列现在是列表类型,这并不理想。它也不再显示出来;我们只是在显示为data.frame的一部分时看到零。
我们可以通过手动转换列来组合列表组件来解决此问题。不幸的是,unlist()
或do.call(c,...)
的明显方法不起作用:
res <- transform(aggregate(flights.duration~year+flights.type,df,sum,simplify=F),flights.duration=do.call(c,flights.duration));
res;
## year flights.type flights.duration
## 1 2000 HR20 0
## 2 2000 L4 0
res$flights.duration;
## [1] 0 0
isS4(res$flights.duration);
## [1] FALSE
Period
值列表变为平面向量,类似于aggregate()
完成的简化效果。
问题似乎是列表名称,这会阻止c()
调用按预期运行。我们可以使用unname()
解决此问题。所以这是最终的解决方案:
res <- transform(aggregate(flights.duration~year+flights.type,df,sum,simplify=F),flights.duration=do.call(c,unname(flights.duration)));
res;
## year flights.type flights.duration
## 1 2000 HR20 3H 36M 0S
## 2 2000 L4 118M 0S
所以,虽然我们最终到达那里,但我不推荐这个解决方案。 R生态系统的不同派系之间存在太多的复杂性,功能上的差距以及不协调的相互作用。