R-从序列数据构建新变量
这是对问题的更新/跟进。答案概述了他们的产品不符合新的要求 我正在寻找一种有效的方法(R-从序列数据构建新变量,r,dataframe,data.table,sequence,R,Dataframe,Data.table,Sequence,这是对问题的更新/跟进。答案概述了他们的产品不符合新的要求 我正在寻找一种有效的方法(data.table?)为每个ID构造两个新的度量 措施1和措施2需要满足以下条件: 条件1: 查找三行的序列,其中: 第一个计数>0 第二个'count>1'和 第三个计数==1 措施1的条件2: 获取序列第三行的product中元素的值,这些元素是: 在序列第二行的产品中 不在序列第一行的库存中 措施2的条件2: 获取序列最后一行的product中元素的值,这些元素是: 不在序列第二行的产品中 不
data.table
?)为每个ID
构造两个新的度量
措施1和措施2需要满足以下条件:
条件1:
查找三行的序列,其中:
- 第一个
计数>0
- 第二个'count>1'和
- 第三个
李>计数==1
- 获取序列第三行的
中元素的值,这些元素是:product
- 在序列第二行的
产品中
- 不在序列第一行的
库存中
- 获取序列最后一行的
中元素的值,这些元素是:product
- 不在序列第二行的
产品中
- 不在序列第一行的
库存中
您将如何编写此代码?要做到这一点,您需要知道几件事:
用于比较组中的值shift
函数用于分割字符串以进入标准化数据视图separate_rows
库(data.table)
dt 1&count.3==1]
dt我不确定高效的标准是什么,但这里有一种使用embed
和tidyverse
风格的方法。它会过滤,所以你的工作越来越少
加载数据和包(注意后面的setdiff
和intersect
来自dplry
)
您可以将其转换回最终产品、以前的产品和感兴趣的库存行,以通过添加偏移来确定度量。将它们拆分为单个组件的列表
finalProds <- df1$product[cond1Match + 2] %>%
strsplit(",")
prevProds <- df1$product[cond1Match + 1] %>%
strsplit(",")
initialStock <- df1$stock[cond1Match] %>%
strsplit(",")
这将生成所需的输出,每个ID最多只能有一行。在原始帖子中,您指定要报告所有内容。删除切片
调用将产生
#> ID seq1 seq2 seq3 measure1 measure2
#> 1 1 2 3 4 C E
#> 2 1 6 7 1
#> 3 2 1 2 3
#> 4 2 3 1 2 C
#> 5 3 2 3 4 D
如果您希望真正压缩效率,您可以通过将finalProds
、prevProds
和initialStock
的定义放在前面而不是将它们分配给变量来获得一些效率。我可以想象,除非你的匹配集真的很大,否则它可以忽略不计 使用数据的滚动窗口方法。表
和j中的基本R代码
:
library(data.table)
cols <- c("product", "stock")
setDT(df2)[, (cols) := lapply(.SD, function(x) strsplit(as.character(x), split=",")), .SDcols=cols]
ans <- df2[,
transpose(lapply(1L:(.N-2L), function(k) {
if(count[k]>0 && count[k+1L]>1 && count[k+2L]==1) {
m1 <- setdiff(intersect(product[[k+2L]], product[[k+1L]]), stock[[k]])
m2 <- setdiff(setdiff(product[[k+2L]], product[[k+1L]]), stock[[k]])
c(seq1=seqs[k], seq2=seqs[k+1L], seq3=seqs[k+2L],
measure1=if(length(m1) > 0) paste(m1, collapse=",") else "",
measure2=if(length(m2) > 0) paste(m2, collapse=",") else "")
}
}), ignore.empty=TRUE),
ID]
setnames(ans, names(ans)[-1L], c(paste0("seq", 1:3), paste0("measure", 1:2)))
ans
这很有趣。滚动窗口是否有效?也就是说,它是否能够很好地扩展到包含500.000多行的数据?我认为您的问题需要滚动窗口。可能还有比这更有效的方法。您可能希望在实际数据集上进行尝试。这种方法适用于模拟数据。但在实际数据(500000多行)中,product和stock列中填充了数百项。因此,当我们分离行时,dt
变得非常大,我的本地机器(128GB)内存不足。单独的_行
是否有替代方案?也许是一个regex
解决方案?好吧,你可以做几件事:1。对数据进行分区,并分块解决问题(也就是说,将其缩小)2。将这些数据放入像BigQuery(又称委托)这样的数据库中,然后使用SQL。3.检查其他一些软件包,例如,回答此处也请参见D3方法:
library(purrr)
library(dplyr)
df1 <- data.frame(ID = c(1,1,1,1,1,1,1,2,2,2,3,3,3,3),
seqs = c(1,2,3,4,5,6,7,1,2,3,1,2,3,4),
count = c(2,1,3,1,1,2,3,1,2,1,3,1,4,1),
product = c("A", "B", "C", "A,C,E", "A,B",
"A,B,C", "D", "A", "B", "A", "A",
"A,B,C", "D", "D"),
stock = c("A", "A,B", "A,B,C", "A,B,C,E", "A,B,C,E",
"A,B,C,E", "A,B,C,D,E", "A", "A,B", "A,B", "A",
"A,B,C", "A,B,C,D", "A,B,C,D"),
stringsAsFactors = FALSE)
meetsCond1 <- function(rseg) {
seg <- rev(rseg)
all(seg[1] > 0, seg[2] > 1, seg[3] == 1)
}
cond1Match<- embed(df1$count, 3) %>%
apply(1, meetsCond1) %>%
which()
finalProds <- df1$product[cond1Match + 2] %>%
strsplit(",")
prevProds <- df1$product[cond1Match + 1] %>%
strsplit(",")
initialStock <- df1$stock[cond1Match] %>%
strsplit(",")
notStock <- map2(finalProds, initialStock, ~.x[!(.x %in% .y)])
data.frame(ID = df1$ID[cond1Match],
seq1 = df1$seqs[cond1Match],
seq2 = df1$seqs[cond1Match + 1],
seq3 = df1$seqs[cond1Match + 2],
measure1 = imap_chr(notStock,
~intersect(.x, prevProds[[.y]]) %>%
{if(length(.) == 0) "" else paste(., sep = ",")}
),
measure2 = imap_chr(notStock,
~setdiff(.x, prevProds[[.y]]) %>%
{if(length(.) == 0) "" else paste(., sep = ",")}
),
stringsAsFactors = FALSE
) %>%
slice(match(unique(ID), ID))
#> ID seq1 seq2 seq3 measure1 measure2
#> 1 1 2 3 4 C E
#> 2 1 6 7 1
#> 3 2 1 2 3
#> 4 2 3 1 2 C
#> 5 3 2 3 4 D
library(data.table)
cols <- c("product", "stock")
setDT(df2)[, (cols) := lapply(.SD, function(x) strsplit(as.character(x), split=",")), .SDcols=cols]
ans <- df2[,
transpose(lapply(1L:(.N-2L), function(k) {
if(count[k]>0 && count[k+1L]>1 && count[k+2L]==1) {
m1 <- setdiff(intersect(product[[k+2L]], product[[k+1L]]), stock[[k]])
m2 <- setdiff(setdiff(product[[k+2L]], product[[k+1L]]), stock[[k]])
c(seq1=seqs[k], seq2=seqs[k+1L], seq3=seqs[k+2L],
measure1=if(length(m1) > 0) paste(m1, collapse=",") else "",
measure2=if(length(m2) > 0) paste(m2, collapse=",") else "")
}
}), ignore.empty=TRUE),
ID]
setnames(ans, names(ans)[-1L], c(paste0("seq", 1:3), paste0("measure", 1:2)))
ans
ID seq1 seq2 seq3 measure1 measure2
1: 1 2 3 4 C E
2: 2 1 2 3
3: 3 2 3 4 D