如何使用data.table在if-else条件下逐行应用函数?

时间:2017-09-29 21:55:57

标签: r dataframe data.table

我需要在下面的数据框中应用函数。 '天'是销售价值。我需要根据抵消价值将销售转移到正确的位置。例如,在第1行中,偏移量为1,我需要将销售额转移1天,如果为0,则不需要转移,此后也是如此。

id <- c('a', 'b', 'c', 'd', 'e', 'f')  
offset <- c(1,2,3,0,0,2)
day1 <-   c(1,2,3,4,5, 0)
day2 <-   c(1,2,3,4,5, 2)
day3 <-   c(1,2,3,4,5, 6)
day4 <-   c(1,2,3,4,5, 6)
day5 <-   c(1,2,0,4,5, 0)
day6 <-   c(1,0,0,0,5, 0)
day7 <-   c(0,0,0,0,0, 0)
df <- data.frame(id, offset, day1, day2, day3, day4, day5, day6, day7) 

> df
id offset day1 day2 day3 day4 day5 day6 day7
a      1    1    1    1    1    1    1    0
b      2    2    2    2    2    2    0    0
c      3    3    3    3    3    0    0    0
d      0    4    4    4    4    4    0    0
e      0    5    5    5    5    5    5    0
f      2    0    2    6    6    0    0    0

结果应如下所示:

> result
id offset day1 day2 day3 day4 day5 day6 day7
a      1    0    1    1    1    1    1    1
b      2    0    0    2    2    2    2    2
c      3    0    0    0    3    3    3    3
d      0    4    4    4    4    4    0    0
e      0    5    5    5    5    5    5    0
f      2    0    0    0    2    6    6    0

我打算在data.table中使用以下伪函数:

shiftSales = function(df){
 if (start > 0) 
 { 
  then_no_shift 
 }
 else
 { 
  offset_by_offset_value
 }
   return(shift_df)  
}
result <- df(,shiftSales(df), by = "id")

注意:如果没有data.table就可以,我很好。但我的数据很大,所以我认为data.table方法会更快。

3 个答案:

答案 0 :(得分:2)

OP要求逐列逐行移位数据,每个行都有一个特定的偏移量。 (不幸的是,提及 if-else 的标题有点误导)。

到目前为止发布的所有解决方案都使用t()函数(矩阵转置),它表明数据的存储方式并不特别适合此类操作。

下面的解决方案使用melt()上应用shift()操作之前,将数据从长格式重新整形为

library(data.table)
# reshape from wide to long
melt(setDT(df), measure.vars = patterns("^day"))[
  # shift values for each id by its individual offset
  , value := shift(value, offset, fill = 0), by = id][
    # reshape to wide format agian for comparison
    , dcast(.SD, id + offset ~ variable)]
   id offset day1 day2 day3 day4 day5 day6 day7
1:  a      1    0    1    1    1    1    1    1
2:  b      2    0    0    2    2    2    2    2
3:  c      3    0    0    0    3    3    3    3
4:  d      0    4    4    4    4    4    0    0
5:  e      0    5    5    5    5    5    5    0
6:  f      2    0    0    0    2    6    6    0

警告:假设id是唯一的。否则,需要引入额外的行号。

如前所述,我建议重新考虑数据的存储方式。

目前,纵向数据,即day1day2day3,...,以宽格式存储在数据帧中,即,在单独的列中。这并不理想,因为它需要跨列执行逐行操作。

相反,数据可以存储为矩阵(id作为行名,offset存储在单独的向量中)。或者,如果在长格式的data.frame中有其他未公开的列。

答案 1 :(得分:1)

不需要复杂的if/else。请尝试以下方法。

df <- data.frame(id, offset, day1, day2, day3, day4, day5, day6, day7)

df[-(1:2)] <- t(apply(df[-1], 1, function(x) c(rep(0, x[1]), x[2:8])[1:7]))
df

答案 2 :(得分:0)

以下是使用data.table的解决方案:

# read df
df = read.table(
  header = TRUE,
  text = 
    "
    id offset day1 day2 day3 day4 day5 day6 day7
    a      1    1    1    1    1    1    1    0
    b      2    2    2    2    2    2    0    0
    c      3    3    3    3    3    0    0    0
    d      0    4    4    4    4    4    0    0
    e      0    5    5    5    5    5    5    0
    f      2    0    2    6    6    0    0    0
    "
);

# load binhf (for shift function)
library(binhf);

# convert to data table
dt = setDT(df)[
  ,
  # establish the new columns using mapply
  c("day_1", "day_2", "day_3", "day_4", "day_5", "day_6", "day_7") :=
    data.table(
      t(
        x = mapply(
          FUN = function(offset, day_1, day_2, day_3, day_4, day_5, day_6, day_7){
            # make a vector of day_1, day_2, day_3,...,day_7
            vec = c(day_1, day_2, day_3, day_4, day_5, day_6, day_7);

            # shift to the right
            vec_s = shift(v = vec, places = offset, dir = "right");

            # return vec_s
            vec_s;
          },
          # parse vectors
          day_1 = day_1,
          day_2 = day_2,
          day_3 = day_3,
          day_4 = day_4,
          day_5 = day_5,
          day_6 = day_6,
          day_7 = day_7,
          offset = offset
        )
      )
    )
  ][
  # remove unwanted (unshifted) columns
    ,
    -(2:9),
    with = FALSE
  ];

# print dt
dt;

   id day_1 day_2 day_3 day_4 day_5 day_6 day_7
1:  a     0     1     1     1     1     1     1
2:  b     0     0     2     2     2     2     2
3:  c     0     0     0     3     3     3     3
4:  d     4     4     4     4     4     0     0
5:  e     5     5     5     5     5     5     0
6:  f     0     0     0     2     6     6     0

我希望这有帮助!