根据位置减去常数

时间:2019-02-25 20:09:11

标签: r

我有一个DataFrame,其信息存储如下:

id  store   v1  v2  v3  v4  v5  pos
1   A       5   5   7   7   7   3
2   B       1   1   1   4   5   4

我想根据可变位置减去值,例如对于id = 1,位置为3,所以我想从v3,v4和v5中减去一个等于V3和v2之差为2的常数(7-5)。因此,结果数据框应如下所示:

id  store   v1  v2  v3  v4  v5  pos
1   A       5   5   5   5   5   3
2   B       1   1   1   1   2   4

对于第二行,位置为4,所以(V4-V3 = 3),所以我们从位置4和5处的变量中减去3。

谢谢!

4 个答案:

答案 0 :(得分:2)

需要几行,但是可以使用tidyverse中的函数来完成。最大的关键是获取广泛的数据并使其更长(这是gather的工作)。然后我们找到pos == var_pos的位置,计算适当的差,然后从适当的值中减去。使用separateunite函数,我们可以转换v1-> v1,然后再次返回。

library(tidyverse)
dat %>%
    gather(variable, value, starts_with('v')) %>% # wide to long
    separate(variable, c('variable', 'var_pos'), sep = 1) %>% # v1 -> v, 1
    group_by(id) %>%
    mutate(var_diff = value[var_pos == pos] - value[var_pos == (pos - 1)]) %>%
    mutate(value = ifelse(var_pos >= pos, value - var_diff, value)) %>% # subtract
    unite(variable, variable, var_pos, sep = '') %>% # v,1 -> v1
    select(-var_diff) %>%
    spread(variable, value) # long to wide

    id store   pos    v1    v2    v3    v4    v5
  <int> <chr> <int> <int> <int> <int> <int> <int>
1     1 A         3     5     5     5     5     5
2     2 B         4     1     1     1     1     2

答案 1 :(得分:1)

如果数据帧不是太大,那么简单的for循环也可以轻松地实现基数R中的技巧:

# Load your data frame
df <- read.table(header = TRUE, text = "
id  store   v1  v2  v3  v4  v5  pos
1   A       5   5   7   7   7   3
2   B       1   1   1   4   5   4")

# Run through all rows
for (i in seq_len(nrow(df))) {
  p <- df$pos[i]  # Get position
  dif <- df[i, paste0("v", p)]  - df[i, paste0("v", p - 1)]  # Compute difference
  cols <- paste0("v", seq(p, 5))  # Construct colnames to subtract dif from
  df[i, cols] <- df[i, cols] - dif  # Do the subtraction
}
print(df)
#  id store v1 v2 v3 v4 v5 pos
#1  1     A  5  5  5  5  5   3
#2  2     B  1  1  1  1  2   4

当然,此代码基于一些假设,即您的实际生活数据看起来与此处的数据非常相似。如果没有,我想代码很容易采用。

如果您讨厌R中的for循环,则可以将其包装到一个函数中,并使用apply隐藏它。

答案 2 :(得分:1)

#select v columns (v1, v2, ..., v5)
vs <- df[grep('^v', names(df))]
# compute differences (in this case, the vector c(2, 3))
diffs <- vs[cbind(1:nrow(df), df$pos)] - vs[cbind(1:nrow(df), df$pos - 1)]
# subtract diffs from vs if the column is >= pos
df[grep('^v', names(df))] <- vs - diffs*(col(vs) >= df$pos)


df
#   id store v1 v2 v3 v4 v5 pos
# 1  1     A  5  5  5  5  5   3
# 2  2     B  1  1  1  1  2   4

使用的数据:

df <- read.table(text = '
id  store   v1  v2  v3  v4  v5  pos
1   A       5   5   7   7   7   3
2   B       1   1   1   4   5   4
', header = T)

答案 3 :(得分:0)

另一种tidyverse可能性是:

df %>%
 gather(var, val, -c(id, pos, store)) %>%
 arrange(id, var) %>%
 group_by(id) %>%
 mutate(temp = cumsum(ifelse(parse_number(var) == pos, 1, 0) == 1),
        val = ifelse(temp == 1, 
                       val - (val[min(which(temp == 1))] - val[max(which(temp == 0))]), val)) %>%
 select(-temp) %>%
 spread(var, val)

     id store   pos    v1    v2    v3    v4    v5
  <int> <chr> <int> <int> <int> <int> <int> <int>
1     1 A         3     5     5     5     5     5
2     2 B         4     1     1     1     1     2

首先,它将数据从宽格式转换为长格式,不包括变量“ id”,“ pos”和“ store”。其次,它根据“ id”和“ var”(这是关键字)排列数据,并按“ id”进行分组。第三,它检查(使用变量“ temp”)键中的数字(即变量名“ v1”至“ v5”中的数字)是否等于“ pos”中的数字。如果是这样,它将分配1,然后在1附近执行一个累加和,从而也为所有后续行分配值1。再者,如果“ temp”中的值为1,则将从该值减去最后一行中的值0。在第一行中添加1,然后从所有行中减去1。最后,它将数据恢复为原始形状。