获得head()和tail(),其中NA存在于许多变量中

时间:2015-11-05 13:10:54

标签: r data.table dplyr tidyr

我有几十个变量,其中很多都有缺失值,包括第一次和最后一次观察。我想要一个新的数据集,每个人包含每个变量的第一个和最后一个观察,忽略缺失。

下面的代码做到了,但我希望1)有一些类似于head()的函数,但不必手动删除NA,2)编写函数的方法dplyr' s summarize_each()可用于自动化数据集中的所有变量(当然不是id

set.seed(23331)
df <- data.frame(id=rep(c(1,2,3,4), each = 5),
                 a = c(NA, rnorm(4), rnorm(3), rep(NA, 2), rnorm(4), rep(NA, 5), rnorm(1)),
                 b = c(rep(NA, 2), rnorm(14), rep(NA, 3), rnorm(1)))
df %>% group_by(id) %>% summarise(a.head=head(a[!is.na(a)], n=1), 
                                  a.tail=tail(a[!is.na(a)], n=1),
                                  b.head=head(b[!is.na(b)], n=1), 
                                  b.tail=tail(b[!is.na(b)], n=1)) %>% 
  gather("type", "value", -id) %>% 
  separate(type, into = c("variable", "time"), sep = "\\.") %>% 
  spread(variable, value)

我希望获得dplyr解决方案,但如果其中一个解决方案是最佳解决方案,那么我会采用basedata.table解决方案。

期望的输出:

来源:本地数据框[8 x 4]

     id  time          a          b
  (dbl) (chr)      (dbl)      (dbl)
1     1  head -0.5877282  0.4975612
2     1  tail -0.7904277 -0.3860010
3     2  head  0.5872134 -0.3923887
4     2  tail -0.3222003  0.3114662
5     3  head -0.2553290  0.7521095
6     3  tail  0.3095699 -0.9113326
7     4  head -0.3809334  1.4752274
8     4  tail -0.3809334  3.2767918

4 个答案:

答案 0 :(得分:2)

我们将'data.frame'转换为'data.table'(setDT(df)),按'id'分组,我们循环遍历Data.table的子集(lapply(.SD,..)并领导每列的headtail

library(data.table)
f1 <- function(x, n) {x1 <- x[!is.na(x)]; c(head(x1,n), tail(x1,n))}
setDT(df)[,lapply(.SD, f1, n=1) ,id][, time:= c('head', 'tail')][]

或使用melt/dcast

 DT <- setDT(df)[,melt(lapply(.SD, function(x) list(head=head(x[!is.na(x)],1),
              tail=tail(x[!is.na(x)],1)))) ,id]
 dcast(DT, id+L2~L1, value.var='value')

答案 1 :(得分:1)

dplyr不适用于导致除1n()以外的多个行的转换。

要留在那个世界,你可以使用(据我所见)低效do

library(magrittr)
ht_nona = . %>% na.omit %>% { c(first(.), dplyr::last(.)) }

df %>% group_by(id) %>% do( as.data.frame(lapply(., ht_nona)) )

另一个(可能更糟糕的)选项是summarise两次并绑定行:

bind_rows(
  df %>% group_by(id) %>% summarise_each(funs(. %>% na.omit %>% first)),
  df %>% group_by(id) %>% summarise_each(funs(. %>% na.omit %>% (dplyr::last)))
)

答案 2 :(得分:1)

缺点是这个需要三个

set.seed(23331)
df <- data.frame(id=rep(c(1,2,3,4), each = 5),
                 a = c(NA, rnorm(4), rnorm(3), rep(NA, 2), rnorm(4), rep(NA, 5), rnorm(1)),
                 b = c(rep(NA, 2), rnorm(14), rep(NA, 3), rnorm(1)))

library('base')
library('utils')
library('stats')

data.frame(id = rep(1:4, each = 2), time = c('head', 'tail'), 
           sapply(df[, -1], function(x) unlist(tapply(x, df$id, FUN = function(y)
             c(head(na.omit(y), 1), tail(na.omit(y), 1))))))

#    id time          a          b
# 11  1 head -0.5877282  0.4975612
# 12  1 tail -0.7904277 -0.3860010
# 21  2 head  0.5872134 -0.3923887
# 22  2 tail -0.3222003  0.3114662
# 31  3 head -0.2553290  0.7521095
# 32  3 tail  0.3095699 -0.9113326
# 41  4 head -0.3809334  1.4752274
# 42  4 tail -0.3809334  3.2767918

答案 3 :(得分:0)

@ akrun答案的变体,同样是data.table:

library(data.table)

setDT(df)[, c(
  list(time=c("head","tail")), 
  lapply(.SD, function(v) setDT(list(v))[!is.na(V1)][c(1,.N), V1] )
), by=id]

   id time          a          b
1:  1 head -0.5877282  0.4975612
2:  1 tail -0.7904277 -0.3860010
3:  2 head  0.5872134 -0.3923887
4:  2 tail -0.3222003  0.3114662
5:  3 head -0.2553290  0.7521095
6:  3 tail  0.3095699 -0.9113326
7:  4 head -0.3809334  1.4752274
8:  4 tail -0.3809334  3.2767918

setDT(list(v)) borrowed from @eddi