我有一个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。
谢谢!
答案 0 :(得分:2)
需要几行,但是可以使用tidyverse中的函数来完成。最大的关键是获取广泛的数据并使其更长(这是gather
的工作)。然后我们找到pos
== var_pos
的位置,计算适当的差,然后从适当的值中减去。使用separate
和unite
函数,我们可以转换v1
-> v
和1
,然后再次返回。
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。最后,它将数据恢复为原始形状。