我有一个名为df
的data.frame,在4列上有800万次观察:
name <- c("Pablo", "Christina", "Steve", "Diego", "Ali", "Brit", "Ruth", "Mia", "David", "Dylan")
year <- seq(2000, 2009, 1)
v1 <- sample(1:10, 10, replace=T)
v2 <- sample(1:10, 10, replace=T)
df <- data.frame(year, v1)
> df
name year v1 v2
1 Pablo 2000 2 9
2 Christina 2001 5 3
3 Steve 2002 8 9
4 Diego 2003 7 6
5 Ali 2004 2 4
6 Brit 2005 1 1
7 Ruth 2006 10 9
8 Mia 2007 6 7
9 David 2008 10 9
10 Dylan 2009 3 2
我想生成一个data.frame output
,其中df
中所有行的成对组合如下所示:
>output
name year v1 v2 name_2 year_2 v1_2 v2_2
1 Pablo 2000 2 9 Christina 2001 5 3
2 Pablo 2000 2 9 Steve 2002 8 9
3 Pablo 2000 2 9 Diego 2003 7 6
etc.
最快的方法是什么?
答案 0 :(得分:4)
tidyr::crossing
将返回所有观察组合,但您需要使用setNames
等设置名称。如果您不想进行自我匹配,可以通过在任何唯一ID列上调用dplyr::filter
来删除它们。
library(tidyverse)
df_crossed <- df %>%
setNames(paste0(names(.), '_2')) %>%
crossing(df) %>%
filter(name != name_2)
head(df_crossed)
## name_2 year_2 v1_2 v2_2 name year v1 v2
## 1 Pablo 2000 5 5 Christina 2001 7 3
## 2 Pablo 2000 5 5 Steve 2002 1 9
## 3 Pablo 2000 5 5 Diego 2003 2 8
## 4 Pablo 2000 5 5 Ali 2004 9 5
## 5 Pablo 2000 5 5 Brit 2005 8 5
## 6 Pablo 2000 5 5 Ruth 2006 8 1
修复名称的另一种方法是在janitor::clean_names
之后使用crossing
,尽管它是一个额外的包。
答案 1 :(得分:2)
希望这会给帖子所有者正在寻找的结果。
.controller('CM_prendasPorM2Ctrl', function($scope, $state, cssInjector, $http) {
$scope.goToWebsite = function(address){
window.open(address, '_blank');
};
答案 2 :(得分:2)
不要添加噪声,而是考虑在同一数据帧上使用merge
的基本R交叉连接,同时过滤掉反向重复项。请注意,过滤器之前的交叉连接将返回8 mill X 8 mill记录数据集,因此希望您的RAM足以进行此类操作。
df <- data.frame(name = c("Pablo", "Christina", "Steve", "Diego", "Ali",
"Brit", "Ruth", "Mia", "David", "Dylan"),
year = seq(2000, 2009, 1),
v1 =sample(1:10, 10, replace=T),
v2 =sample(1:10, 10, replace=T),
stringsAsFactors = FALSE)
# MERGE ON KEY, THEN REMOVE KEY COL
df$key <- 1
dfm <- merge(df, df, by="key")[,-1]
# FILTER OUT SAME NAME AND REVERSE DUPS, THEN RENAME COLUMNS
dfm <- setNames(dfm[(dfm$name.x < dfm$name.y),],
c("name_p1", "year_p1", "V1_p1", "V2_p1",
"name_p2", "year_p2", "V1_p2", "V2_p2"))
# ALL PABLO PAIRINGS
dfm[dfm$name_p1=='Pablo' | dfm$name_p2=='Pablo',]
# name_p1 year_p1 V1_p1 V2_p1 name_p2 year_p2 V1_p2 V2_p2
# 3 Pablo 2000 7 8 Steve 2002 3 1
# 7 Pablo 2000 7 8 Ruth 2006 8 4
# 11 Christina 2001 10 10 Pablo 2000 7 8
# 31 Diego 2003 4 9 Pablo 2000 7 8
# 41 Ali 2004 5 3 Pablo 2000 7 8
# 51 Brit 2005 2 4 Pablo 2000 7 8
# 71 Mia 2007 7 7 Pablo 2000 7 8
# 81 David 2008 1 7 Pablo 2000 7 8
# 91 Dylan 2009 9 2 Pablo 2000 7 8
如果以某种方式从SQL兼容的数据库派生这个大集合,我可以提供SQL中的对应部分,因为过滤器使用连接过程运行而不是单独运行,因此可能更有效。
答案 3 :(得分:1)
您可以使用data.table
将名称列交叉连接到自身,并删除重复的案例。这将导致一个较小的结构,在该结构上合并数据而不是完全合并,然后进行过滤。您可以使用两个合并添加其余数据:一次合并与第一个名称列关联的数据,再次合并与第二列关联的数据。
name <- c("Pablo", "Christina", "Steve", "Diego", "Ali", "Brit", "Ruth", "Mia", "David", "Dylan")
year <- seq(2000, 2009, 1)
v1 <- sample(1:10, 10, replace=T)
v2 <- sample(1:10, 10, replace=T)
# stringsAsFactors = FALSE in order for pmin to work properly
df <- data.frame(name, year, v1, v2, stringsAsFactors = FALSE)
library(data.table)
setDT(df)
setkey(df)
# cross-join name column to itself while removing duplicates and redundancies
name_cj <- setnames(
CJ(df[, name], df[, name])[V1 < V2], # taking a hint from Parfait's clever solution
c("name1", "name2"))
# perform 2 merges, once for the 1st name column and
# again for the 2nd name colum
name_cj <- merge(
merge(name_cj, df, by.x = "name1", by.y = "name"),
df,
by.x = "name2", by.y = "name", suffixes = c("_1", "_2"))
# reorder columns as desired with setorder()
head(name_cj)
# name2 name1 year_1 v1_1 v2_1 year_2 v1_2 v2_2
#1: Brit Ali 2004 3 8 2005 4 5
#2: Christina Ali 2004 3 8 2001 9 8
#3: Christina Brit 2005 4 5 2001 9 8
#4: David Ali 2004 3 8 2008 5 2
#5: David Brit 2005 4 5 2008 5 2
#6: David Christina 2001 9 8 2008 5 2
答案 4 :(得分:1)
@alistaires解决方案的扩展显示了用作索引的交叉矩阵。所述问题需要完整的交叉输出 将是非常大的(大约6400万行,800万项)所以那里 真的没办法满足内存要求。但是,如果 现实世界的用法是处理子集,索引技术 这里显示的可能是一种减少内存使用的方法。跨越整数只能在交叉操作期间使用较少的内存。
library(dplyr)
library(tidyr)
crossed <- as.matrix(crossing(1:nrow(df), 1:nrow(df)))
# bind and name in one step (may be inefficient) so that filter can be applied in one step
output <- as.data.frame(cbind(df[crossed[, 1],],
data.frame(name_2 = df[crossed[, 2], 1],
year_2 = df[crossed[, 2], 2],
v1_2 = df[crossed[, 2], 3],
v2_2 = df[crossed[, 2], 4]) )) %>%
filter(!(name == name_2 & year == year_2))
# estimated sized for 8 million rows gine this 10 row sample
format(object.size(output) / (10 / 8e6), units="MB")
#[1] "5304 Mb"