我是R的新手,这是我关于堆栈溢出的第一个问题。
我正在努力
示例数据:
id code date_down date_up
1: 1 p 2019-01-01 2019-01-02
2: 1 f 2019-01-02 2019-01-03
3: 2 f 2019-01-02 2019-01-02
4: 2 p 2019-01-03 <NA>
5: 3 p 2019-01-04 <NA>
6: 4 <NA> 2019-01-05 2019-01-05
7: 5 f 2019-01-07 2019-01-08
8: 5 p 2019-01-07 2019-01-08
9: 5 p 2019-01-09 2019-01-09
10: 6 f 2019-01-10 2019-01-10
11: 6 p 2019-01-10 2019-01-10
12: 6 p 2019-01-10 2019-01-11
我想做的是
id
date_up
,code='p'
和date-up
(找到的行)大于我正在更新的行的date-down
我的预期结果是:
id code date_down date_up founddate
1: 1 p 2019-01-01 2019-01-02 <NA>
2: 1 f 2019-01-02 2019-01-03 <NA>
3: 2 f 2019-01-02 2019-01-02 <NA>
4: 2 p 2019-01-03 <NA> <NA>
5: 3 p 2019-01-04 <NA> <NA>
6: 4 <NA> 2019-01-05 2019-01-05 <NA>
7: 5 f 2019-01-07 2019-01-08 2019-01-08
8: 5 p 2019-01-07 2019-01-08 2019-01-09
9: 5 p 2019-01-09 2019-01-09 <NA>
10: 6 f 2019-01-10 2019-01-10 2019-01-11
11: 6 p 2019-01-10 2019-01-10 2019-01-11
12: 6 p 2019-01-10 2019-01-11 <NA>
我使用<code>尝试了许多变体。SD,。N
,创建一个新的列,其中包含DT[,idcount:=seq_leg(.N),by=id]
但实际上没有得到任何结果。非常感谢任何帮助。
也有好的参考资料。表:)非常感谢
编辑:我已经编辑了提供的原始数据,以给出一个更微妙的例子,其中第10行使用第12行的数据更新,因为第12行在id子集中并满足资格标准。第11行不符合资格标准,因此数据不用于更新第10行。还包括我第一次使用dput
!
示例数据作为 dput
代码:
dt <- structure(list(
id = c(1L, 1L, 2L, 2L, 3L, 4L, 5L, 5L, 5L, 6L, 6L, 6L),
code = c("p", "f", "f", "p", "p", "<NA>", "f", "p", "p", "f", "p", "p"),
date_down = structure(c(17897, 17898, 17898, 17899, 17900, 17901, 17903, 17903, 17905, 17906, 17906, 17906), class = "Date"),
date_up = structure(c(17898, 17899, 17898, NA, NA, 17901, 17904, 17904, 17905, 17906, 17906, 17907), class = "Date")),
class = c("data.table", "data.frame"),
row.names = c(NA, -12L))
setDT(dt) # to reinit the internal self ref pointer (known issue)
这是一种快速而肮脏的方法,不需要您进行太多思考,并捕获子集中的第一个可行选项,如果不存在,则留下一个<code>NA</code>。
do(f(.))
调用计算由 group_by
语句定义的每个 dt
子集上的预定义函数 f
。我会把这个简单的脚本翻译成Rcpp
,以便认真使用。
library(dplyr)
f <- function(x){
x <- x %>% mutate(founddate = as.Date(NA))
for(i in 1:nrow(x)){
y <- x[i, "date_down"]
x[i, "founddate"] <-(x[-c(1:i),] %>% filter(code == "p", date_up > y) %>% select(date_up))[1, ]
}
return(x)
}
dt %>% group_by(id) %>% do(f(.))
# A tibble: 12 x 5
# Groups: id [6]
id code date_down date_up founddate
<int> <chr> <date> <date> <date>
1 1 p 2019-01-01 2019-01-02 NA
2 1 f 2019-01-02 2019-01-03 NA
3 2 f 2019-01-02 2019-01-02 NA
4 2 p 2019-01-03 NA NA
5 3 p 2019-01-04 NA NA
6 4 <NA> 2019-01-05 2019-01-05 NA
7 5 f 2019-01-07 2019-01-08 2019-01-08
8 5 p 2019-01-07 2019-01-08 2019-01-09
9 5 p 2019-01-09 2019-01-09 NA
10 6 f 2019-01-10 2019-01-10 2019-01-11
11 6 p 2019-01-10 2019-01-10 2019-01-11
12 6 p 2019-01-10 2019-01-11 NA
你对糟糕表现的评论并不奇怪。如果我知道怎么做,我会给这个个人消息,但下面是一个Rcpp::cpp函数
来做同样的事情。
Rcpp::cppFunction('DataFrame fC(DataFrame x) {
int i, j;
int n = x.nrows();
CharacterVector code = x["code"];
DateVector date_up = x["date_up"];
DateVector date_down = x["date_down"];
DateVector founddate = rep(NA_REAL, n);
for(i = 0; i < n; i++){
for(j = i + 1; j < n; j++){
if(code(j) == "p"){
if(date_up(j) > date_down(i)){
founddate(i) = date_up(j);
break;
} else{
continue;
}
} else{
continue;
}
}
}
x.push_back(founddate, "founddate");
return x;
}')
dt %>% group_by(id) %>% do(fC(.))
非数据表方式方法:
> df <- structure(list(
+ id = c(1L, 1L, 2L, 2L, 3L, 4L, 5L, 5L, 5L, 6L, 6L, 6L),
+ code = c("p", "f", "f", "p", "p", "<NA>", "f", "p", "p", "f", "p", "p"),
+ date_down = structure(c(17897, 17898, 17898, 17899, 17900, 17901, 17903, 17903, 17905, 17906, 17906, 17906), class = "Date"),
+ date_up = structure(c(17898, 17899, 17898, NA, NA, 17901, 17904, 17904, 17905, 17906, 17906, 17907), class = "Date")),
+ class = c("data.frame"),
+ row.names = c(NA, -12L))
>
>
> Lista <- lapply(split(df, df$id), function(x){
+ x$founddate <-
+ sapply(c(1:nrow(x)), function(y){
+ na.omit(sapply(y:nrow(x), function(i){
+ ifelse(x[i + 1, "code"] == "p" & x[i + 1, "date_up"] > x[y, "date_down"],
+ x[i + 1, "date_up"], NA)
+ }))[1]
+ })
+ x$founddate <- as.Date(x$founddate, origin = "1970-01-01")
+ return(x)
+ })
>
>
> df <- do.call(rbind.data.frame, Lista)
>
> df
id code date_down date_up founddate
1.1 1 p 2019-01-01 2019-01-02 <NA>
1.2 1 f 2019-01-02 2019-01-03 <NA>
2.3 2 f 2019-01-02 2019-01-02 <NA>
2.4 2 p 2019-01-03 <NA> <NA>
3 3 p 2019-01-04 <NA> <NA>
4 4 <NA> 2019-01-05 2019-01-05 <NA>
5.7 5 f 2019-01-07 2019-01-08 2019-01-08
5.8 5 p 2019-01-07 2019-01-08 2019-01-09
5.9 5 p 2019-01-09 2019-01-09 <NA>
6.10 6 f 2019-01-10 2019-01-10 2019-01-11
6.11 6 p 2019-01-10 2019-01-10 2019-01-11
6.12 6 p 2019-01-10 2019-01-11 <NA>
>
在给定的条件下,每行有多个匹配项。建议的答案得到第一个匹配,但这可以修改。
希望有帮助。
>
下面我展示了5个工作<code>数据。表根据OP的实际数据集(140万条记录)进行性能测试的候选解决方案。
所有5个解决方案都在< code>on子句中使用“非等价”连接(使用不等式来比较连接的列)。
每个解决方案只是一个小的渐进式代码更改,因此应该很容易比较不同的< code>data.table选项和语法选择。
为了解决 data.table
语法,我将其分解为 OP 问题的以下步骤:
# Add row numbers to all records in dt (only because you
# have criteria based on comparing sequential rows):
dt[, row := .I]
# Compute result columns ( then standard assignment into dt using <- )
dt$found_date <-
dt[code=='p'][dt, # join dt to the data.table matching your criteria, in this case dt[code=='p']
.( x.date_up ), # columns to select, x. prefix means columns from dt[code=='p']
on = .(id==id, row > row, date_up > date_down), # join criteria: dt[code=='p'] fields on LHS, main dt fields on RHS
mult = "first"] # get only the first match if multiple matches
在上面的连接表达式中请注意:
,i
是您的主dt。通过这种方式,您可以从主 data.表中获取所有记录。x
是要从中查找匹配值的子集(或任何其他 data.table)。结果与请求的输出匹配:
dt
id code date_down date_up row found_date
1: 1 p 2019-01-01 2019-01-02 1 <NA>
2: 1 f 2019-01-02 2019-01-03 2 <NA>
3: 2 f 2019-01-02 2019-01-02 3 <NA>
4: 2 p 2019-01-03 <NA> 4 <NA>
5: 3 p 2019-01-04 <NA> 5 <NA>
6: 4 <NA> 2019-01-05 2019-01-05 6 <NA>
7: 5 f 2019-01-07 2019-01-08 7 2019-01-08
8: 5 p 2019-01-07 2019-01-08 8 2019-01-09
9: 5 p 2019-01-09 2019-01-09 9 <NA>
10: 6 f 2019-01-10 2019-01-10 10 2019-01-11
11: 6 p 2019-01-10 2019-01-10 11 2019-01-11
12: 6 p 2019-01-10 2019-01-11 12 <NA>
注意:如果您愿意,可以通过执行 dt[, 行 := NULL]
来删除行
列。
与上面相同的逻辑来连接和查找结果列,但是现在使用“通过引用分配”< code>:=在< code>dt中创建< code>found_date:
dt[, row := .I] # add row numbers (as in all the solutions)
# Compute result columns ( then assign by reference into dt using :=
# dt$found_date <-
dt[, found_date := # assign by reference to dt$found_date
dt[code=='p'][dt,
.( x.date_up ),
on = .(id==id, row > row, date_up > date_down),
mult = "first"]]
在解决方案2中,将我们的结果“通过引用”分配给dt的微小变化应该比解决方案1更有效。解决方案1以完全相同的方式计算结果-唯一的区别是解决方案1使用标准分配<代码>
与解决方案 2 类似,但现在使用 .(.SD)
代替 dt
来指代原始 dt,而不直接命名它。
dt[, row := .I] # add row numbers (as in all the solutions)
setkey(dt, id, row, date_down) #set key for dt
# For all rows of dt, create found_date by reference :=
dt[, found_date :=
# dt[code=='p'][dt,
dt[code=='p'][.(.SD), # our subset (or another data.table), joined to .SD (referring to original dt)
.( x.date_up ),
on = .(id==id, row > row, date_up > date_down),
mult = "first"] ]
上述SD引用了我们重新分配到的原始dt。它对应于数据的子集。包含在第一个<code>dt[,
注意:在解决方案 3 中,我使用 setkey()
来设置密钥。我应该在解决方案1中这样做
类似于解决方案 3,但使用 .SD 比以前多一次。我们的主 data.table 名称 dt
现在在整个表达式中只出现一次!
# add row column and setkey() as previous solutions
dt[, found_date :=
# dt[code=='p'][.(.SD),
.SD[code=='p'][.SD, # .SD in place of dt at left! Also, removed .() at right (not sure on this second change)
.(found_date = x.date_up),
on = .(id==id, row > row, date_up > date_down),
mult = "first"]]
通过上面的更改data.table名称dt
只出现一次。我非常喜欢它,因为它可以很容易地在其他地方复制、改编和重用。
另请注意:我之前使用的地方。(SD)我现在已经删除了。()围绕。SD
,因为它似乎不需要它。但是对于这个更改,我不确定它是否有任何性能优势,或者它是否是data.table首选语法。如果有人能在这一点上添加评论以提供建议,我将不胜感激。
与以前的解决方案一样,但在加入时使用by
显式地将子集分组到操作上
# add row column and setkey() as previous solutions
dt[, found_date :=
.SD[code=='p'][.SD,
.(found_date = x.date_up),
# on = .(id==id, row > row, date_up > date_down),
on = .(row > row, date_up > date_down), # removed the id column from here
mult = "first"]
, by = id] # added by = id to group the .SD subsets
在最后一个解决方案中,我将其改为使用< code>by子句显式地对。< code>id上的SD子集。
注意:与解决方案1-4相比,解决方案5在OllieB的实际数据方面表现不佳。然而,通过测试我自己的模拟数据,我发现当id
列中的唯一组数较低时,解决方案五可以表现良好:
-在150万条记录中只有6个组,该解决方案的工作速度与其他解决方案一样快
-在150万个记录中,有4万个组,我看到了与OllieB报告类似的糟糕表现。
>
根据 OllieB 的反馈,对于 OllieB 实际数据中的 145 万条记录,解决方案 1 到 4 中的每条记录都经过 2.42 秒或更短的“经过”时间。解决方案 3 对于 OllieB 来说,“已用 = 1.22”秒似乎工作得最快。
我个人更喜欢解决方案4,因为语法更简单。
数据表格版本:1.12.0
R版本3.5.3(2019-03-11)
自动索引,@Arun。 作为您问题的一部分,您要求“对 data.table 的任何良好引用”。我发现以下内容很有帮助:
>
数据。GitHub上的表格入门Wiki是开始的地方。
特别是对于这个问题,值得一读:
重要的是,请注意@Arun的回答,它解释了“实现on=argument的原因”,表明可能不再需要设置键:
因此,重要的是要找出重新排序整个数据所花费的时间。表值得花时间进行高效缓存连接/聚合。通常,除非对相同的键控数据执行重复的分组/连接操作。表中,应该没有明显的差异。
因此,在大多数情况下,不需要再设置键了。我们建议尽可能使用on=,除非设置键可以显着提高您想利用的性能。
>
这个SO问题似乎是关于不同data.table
连接的信息中心:如何连接(合并)数据帧(内部、外部、左侧、右侧)?
最后,数据表备忘单是一个很好的参考(来自在 GitHub 上的“入门维基”上找到的链接)。
一如既往,我很感激任何人的建议,因为这也许可以进一步改善。
如果您可以添加任何内容,请随时发表评论,更正或发布其他解决方案。
如果可能请帮忙。多谢.
我正在尝试创建一个新变量,该变量仅在满足特定条件时才打印一系列列的第一个值。 为了澄清,我的数据库看起来像这样: 我想创建一个变量(main),仅当值不是以C00到C99开头时,才打印第一个var列中的值。如果值确实以该条件开头,那么我想在下一列测试该条件,直到满足条件并打印该值。 因此,对于上表,新创建的变量(main)应如下所示: 我不太确定从哪里开始,但我怀疑这可能涉及突变()和ifelse
问题内容: 我将不胜感激创建此查询的任何帮助。我尝试了好几种方法,但都没有碰到运气。由于我的问题很难表达,因此我将举一个我想做的简单例子。我的数据在结构上类似于以下内容: 如果我有两种特定颜色的ID,我想创建一个查询以返回类型。例如,我想查找所有具有蓝色和红色的ID。然后查询将返回: A和B的返回顺序并不重要。数据集很大,我希望有许多ID可以同时满足这两个条件(也许是50,000个左右)。我要指出
我正在尝试使用一个do while循环来找出用户是想将一只狗还是一只猫检查到Java的养狗系统中。其思想是,当提示时输入“dog”或“cat”,任何输入都将导致错误,并且他们将再次被提示输入文件名。 如果输入了“cat”或“dog”,那么将为程序分配等效的文件(dogs.txt或cats.txt),然后系统将运行并将数据加载到程序中。 以下是当前的变量: 以及导致问题的方法: 下面是运行代码时打印
问题内容: 使用以下模型: 如果我要查找包含至少一篇文章的订单操作,则可以按预期工作: 但是,如果要查找订单中所有商品的订单操作,正确的方法是什么? 引发错误(我理解为什么会这样)。 问题答案: 一个简单的解决方案: 这只是一个查询,但每篇文章都有一个内部联接。对于多篇文章,Willem更巧妙的解决方案应该会表现更好。
我需要在同一个Google Sheets文件中同步所有工作表。我只想同步第一行和第一列(这是冻结的),而其余的内容不应该同步。 我还希望这样,如果我在工作表中插入/删除一行,也会对所有其他同步工作表执行相同的操作。此外,如果进行了其他修改,例如冻结了一行,则应在所有图纸上进行相同的修改。 我看到了这些问题中的代码: 如何在两个Google电子表格中使用=importrange()同步两张工作表 简