使用lapply转置列的一部分并将其作为新列添加到数据框

时间:2018-06-25 22:06:28

标签: r

我一直在寻找一个清晰的例子,但是找不到适合我的情况的东西,因此我构建了一个与这个例子非常相似的DF(但是具有更多的数据,总共超过一百万行)

Key1 <- c("A", "B", "C", "A", "C", "B", "B", "C", "A", "C") 
Key2 <- c("A1", "B1", "C1", "A2", "C2", "B2", "B3", "C3", "A3", "C4") 
NumVal <- c(2, 3, 1, 4, 6, 8, 2, 3, 1, 0)
DF1 <- as.data.frame(cbind(Key1, Key2, NumVal), stringsAsFactors = FALSE) %>% arrange(Key2)
ConsId <- c(1:10)
DF1 <- cbind(DF1, ConsId)

现在,我想做的是在数据框中添加3个新列(在现实生活中我需要12列,但为了在此玩具示例中获得更多图形效果,我们将使用3列) row对应于$ NumVal的值,具有相同的$ Key1且大于或等于$ ConsId与每行中的值相同,并用NA填充剩余的空间,如果我不太清楚,这是预期的结果:

Key1    Key2    NumVal  ConsId  V1  V2  V3
A        A1        2       1    2   4   1
A        A2        4       2    4   1   NA
A        A3        1       3    1   NA  NA
B        B1        3       4    3   8   2
B        B2        8       5    8   2   NA
B        B3        2       6    2   NA  NA
C        C1        1       7    1   6   3
C        C2        6       8    6   3   0
C        C3        3       9    3   0   NA
C        C4        0      10    0   NA  NA

现在我正在使用do.call(rbind),即使它工作得很好,对于我的真实数据来说,它花费的时间太长了,超过了100万行(大约6个小时),我也尝试了bind_rows dplyr函数,但是花费了更长的时间,所以我坚持使用do.call选项,这是我使用的代码示例:

# Function
TranspNumVal <- function(i){
  Id <- DF1[i, "Key1"]
  IdCons <- DF1[i, "ConsId"]
  myvect <- as.matrix(filter(DF1, Id == Key1, ConsId >= IdCons) %>% select(NumVal))
  Result <-  as.data.frame(t(myvect[1:3]))
  return(Result)
}

# Applying the function to the entire data frame
DF2 <- do.call(rbind, lapply(1:NROW(DF1), function(i) TranspNumVal(i)))
DF3 <- cbind(DF1, DF2)

也许更改类导致代码效率低下,或者我只是没有找到一种更好的方法来向量化我的问题(您不希望知道嵌套循环花费了多长时间), '对R来说还很陌生,并且刚刚开始与dplyr鬼混,所以我愿意接受有关如何优化代码的任何建议

3 个答案:

答案 0 :(得分:0)

按“ Key1”分组后,使用shift(来自data.table)来获取list中的下一个“ NumVal”值,将其转换为tibbleunnest嵌套的list元素到数据集的各个列。默认情况下,shift fill不适用。

library(data.table) 
library(tidyverse)
DF1 %>% 
  group_by(Key1) %>% 
  mutate(new = shift(NumVal, 0:(n()-1), type = 'lead') %>% 
                     map(~ 
                          as.list(.x) %>%
                          set_names(paste0("V", seq_along(.))) %>% 
                          as_tibble)) %>% 
  unnest %>%
  select(-V4)
# A tibble: 10 x 7
# Groups:   Key1 [3]
#   Key1  Key2  NumVal ConsId    V1    V2    V3
#   <chr> <chr>  <dbl>  <int> <dbl> <dbl> <dbl>
# 1 A     A1         2      1     2     4     1
# 2 A     A2         4      2     4     1    NA
# 3 A     A3         1      3     1    NA    NA
# 4 B     B1         3      4     3     8     2
# 5 B     B2         8      5     8     2    NA
# 6 B     B3         2      6     2    NA    NA
# 7 C     C1         1      7     1     6     3
# 8 C     C2         6      8     6     3     0
# 9 C     C3         3      9     3     0    NA
#10 C     C4         0     10     0    NA    NA

数据

DF1 <- data.frame(Key1, Key2, NumVal, stringsAsFactors = FALSE) %>% 
                       arrange(Key2)
DF1$ConsId <- 1:10

答案 1 :(得分:0)

一个dplyr管道。

第一个实用程序功能将基于aNumVal)的值过滤bConsId):

myfunc1 <- function(a,b) {
  n <- length(b)
  lapply(seq_along(b), function(i) a[ b >= b[i] ])
}

第二个实用程序功能将衣衫{的list转换为data.frame。它可以添加任意数量的列,但根据您的要求,我们将其限制为3:

myfunc2 <- function(x, ncols = 3) {
  n <- min(ncols, max(lengths(x)))
  as.data.frame(do.call(rbind, lapply(x, `length<-`, n)))
}

现在管道:

dat %>%
  group_by(Key1) %>%
  mutate(lst = myfunc1(NumVal, ConsId)) %>%
  ungroup() %>%
  bind_cols(myfunc2(.$lst)) %>%
  select(-lst) %>%
  arrange(Key1, ConsId)
# # A tibble: 10 × 7
#     Key1  Key2 NumVal ConsId    V1    V2    V3
#    <chr> <chr>  <int>  <int> <int> <int> <int>
# 1      A    A1      2      1     2     4     1
# 2      A    A2      4      2     4     1    NA
# 3      A    A3      1      3     1    NA    NA
# 4      B    B1      3      4     3     8     2
# 5      B    B2      8      5     8     2    NA
# 6      B    B3      2      6     2    NA    NA
# 7      C    C1      1      7     1     6     3
# 8      C    C2      6      8     6     3     0
# 9      C    C3      3      9     3     0    NA
# 10     C    C4      0     10     0    NA    NA

答案 2 :(得分:0)

我们可以使用dplyr::lead

DF1 %>%
    group_by(Key1) %>%
    mutate(
        V1 = NumVal,
        V2 = lead(NumVal, n = 1),
        V3 = lead(NumVal, n = 2))
## A tibble: 10 x 7
## Groups:   Key1 [3]
#   Key1  Key2  NumVal ConsId V1    V2    V3
#   <chr> <chr> <chr>   <int> <chr> <chr> <chr>
# 1 A     A1    2           1 2     4     1
# 2 A     A2    4           2 4     1     NA
# 3 A     A3    1           3 1     NA    NA
# 4 B     B1    3           4 3     8     2
# 5 B     B2    8           5 8     2     NA
# 6 B     B3    2           6 2     NA    NA
# 7 C     C1    1           7 1     6     3
# 8 C     C2    6           8 6     3     0
# 9 C     C3    3           9 3     0     NA
#10 C     C4    0          10 0     NA    NA

说明:我们将条目按Key1分组,然后使用leadNumValV2列的V3值进行移位。 V1只是NumVal的副本。