R 如何从两个具有公共值的列表中创建虚拟矩阵?
在R中,我有多个非常大(大约140e6)的IP地址列表。多个列表之间有许多重叠的IP。我想创建一个数据框或数据表,其中包含ip地址作为行名(无重复项),列表名称作为列,0或1表示该ip是否存在于该列表中 例如,我们有以下两个列表,它们之间有大约%的交集R 如何从两个具有公共值的列表中创建虚拟矩阵?,r,tidyr,reshape2,model.matrix,R,Tidyr,Reshape2,Model.matrix,在R中,我有多个非常大(大约140e6)的IP地址列表。多个列表之间有许多重叠的IP。我想创建一个数据框或数据表,其中包含ip地址作为行名(无重复项),列表名称作为列,0或1表示该ip是否存在于该列表中 例如,我们有以下两个列表,它们之间有大约%的交集 a <- c("192.168.0.1","192.168.0.2","192.168.0.3","192.168.0.4","192.168.0.5","192.168.0.6","192.168.0.7","192.168.0.8","
a <- c("192.168.0.1","192.168.0.2","192.168.0.3","192.168.0.4","192.168.0.5","192.168.0.6","192.168.0.7","192.168.0.8","192.168.0.9","192.168.0.10")
b <- c("192.168.1.1","192.168.1.2","192.168.1.3","192.168.1.4","192.168.0.5","192.168.0.6","192.168.0.7","192.168.0.8","192.168.0.9","192.168.0.10")
我尝试过使用整形2、tidyr、model.matrix、intersect和good ol'for循环。我发现了一些人从数据帧创建虚拟矩阵的例子,但不使用向量名作为列,值作为行名,也不使用重复项 dplyr解决方案:
df <- data.frame("IP" = unique(c(a,b)))
df2 <- df%>%mutate(a = ifelse(df$IP %in% a,1,0),b = ifelse(df$IP %in% b,1,0))
dplyr解决方案:
df <- data.frame("IP" = unique(c(a,b)))
df2 <- df%>%mutate(a = ifelse(df$IP %in% a,1,0),b = ifelse(df$IP %in% b,1,0))
首先,我将介绍两种新的解决方案 具有合并的解决方案
df1 <- merge(data.frame(ip=a,a=1), data.frame(ip=b,b=1),all=TRUE) %>%
set_rownames(.,`[`(.,,'ip')) %>% select(-ip) %>% replace(.,is.na(.),0)
# a b
# 192.168.0.1 1 0
# 192.168.0.10 1 1
# 192.168.0.2 1 0
# 192.168.0.3 1 0
# 192.168.0.4 1 0
# 192.168.0.5 1 1
# 192.168.0.6 1 1
# 192.168.0.7 1 1
# 192.168.0.8 1 1
# 192.168.0.9 1 1
# 192.168.1.1 0 1
# 192.168.1.2 0 1
# 192.168.1.3 0 1
# 192.168.1.4 0 1
对于给定的示例:
# Unit: microseconds
# expr min lq mean median uq max neval
# merge 2368.754 2670.8205 3866.2288 2942.6280 3685.1415 38459.947 100
# merge_dt 4220.084 4702.4700 5547.1978 5222.3705 6239.1685 9170.293 100
# dcast 6153.875 6870.3760 9031.8770 7521.7570 8793.9045 46529.917 100
# spread 4329.090 4814.6610 6023.5993 5313.3275 6301.9890 38972.416 100
# reshape 4376.514 5007.1905 5995.1480 5694.1395 6811.4495 8744.180 100
# akrun 238.893 304.3680 366.0376 327.7265 416.3815 654.744 100
# p_routh 1013.967 1190.9255 1418.8037 1296.7450 1651.7220 2162.775 100
# d.b 133.072 183.8595 228.7220 207.0415 278.1780 417.974 100
举个更大的例子:
140E6的基准测试有点高,所以我尝试使用1E5。我任意选择a和b之间大约50%的重叠
n <- 1E5
set.seed(1)
a <- sample(2*n,n)
b <- sample(2*n,n)
我们看到,对于2个向量,p Routh的解是最快的,dcast
是最快的一般解<代码>合并和数据。但是,对于140E6行,表
可能是最快的
通用解决方案 有希望的最终编辑: 我根据我最好的受限解决方案设计了2个通用解决方案,并在3个大小为10E6的向量上运行它们
merge_dt_gen <- function(...){
args <- as.character(substitute(list(...)))[-1]
dts <- args %>% lapply(.%>% data.table(ip=get(.),key="ip"))
all_ips <- data.table(ip = unique(c(...)),key="ip") # all_ips <- data.table(ip = unique(c(a,b)))
for(dt in dts){
all_ips <- merge(all_ips,dt,all.x = TRUE,by="ip")
}
all_ips %>%
as.data.frame %>%
set_rownames(.,`[`(.,,'ip')) %>%
select(-ip) %>%
setNames(args) %>%
replace(.,!is.na(.),1) %>%
replace(.,is.na(.),0)
}
d_cast_gen <- function(...){
args <- as.character(substitute(list(...)))[-1]
args %>%
lapply(.%>% data.frame(get(.)) %>% setNames(c("src","ip"))) %>%
do.call(rbind,.) %>%
transform(v=1) %>%
dcast(ip ~ src,value.var="v") %>%
replace(.,is.na(.),0) %>%
setNames(gsub("v","",colnames(.))) %>%
set_rownames(.,`[`(.,,'ip')) %>% select(-ip)
}
n <- 10E6
set.seed(1)
a <- sample(2*n,n)
b <- sample(2*n,n)
d <- sample(unique(a,b),n)
microbenchmark(
d_cast_gen = d_cast_gen(a,b,d),
merge_dt_gen = merge_dt_gen(a,b,d),
times = 1
)
# Unit: seconds
# expr min lq mean median uq max neval
# d_cast_gen 70.99771 70.99771 70.99771 70.99771 70.99771 70.99771 1
# merge_dt_gen 47.41809 47.41809 47.41809 47.41809 47.41809 47.41809 1
merge\u dt\u gen%
集合名(参数)%%>%
替换(,!is.na(.),1)%>%
替换(,is.na(.),0)
}
d_cast_gen%data.frame(get(.))%%>%setNames(c(“src”,“ip”))%%>%
do.呼叫(rbind),%%>%
变换(v=1)%>%
dcast(ip~src,value.var=“v”)%%>%
替换(,is.na(.),0)%>%
集合名(gsub(“v”),“”,colnames(.))%>%
设置行名(,`[`,,'ip'))%>%select(-ip)
}
n首先,我将介绍两种新的解决方案
具有合并的解决方案
df1 <- merge(data.frame(ip=a,a=1), data.frame(ip=b,b=1),all=TRUE) %>%
set_rownames(.,`[`(.,,'ip')) %>% select(-ip) %>% replace(.,is.na(.),0)
# a b
# 192.168.0.1 1 0
# 192.168.0.10 1 1
# 192.168.0.2 1 0
# 192.168.0.3 1 0
# 192.168.0.4 1 0
# 192.168.0.5 1 1
# 192.168.0.6 1 1
# 192.168.0.7 1 1
# 192.168.0.8 1 1
# 192.168.0.9 1 1
# 192.168.1.1 0 1
# 192.168.1.2 0 1
# 192.168.1.3 0 1
# 192.168.1.4 0 1
对于给定的示例:
# Unit: microseconds
# expr min lq mean median uq max neval
# merge 2368.754 2670.8205 3866.2288 2942.6280 3685.1415 38459.947 100
# merge_dt 4220.084 4702.4700 5547.1978 5222.3705 6239.1685 9170.293 100
# dcast 6153.875 6870.3760 9031.8770 7521.7570 8793.9045 46529.917 100
# spread 4329.090 4814.6610 6023.5993 5313.3275 6301.9890 38972.416 100
# reshape 4376.514 5007.1905 5995.1480 5694.1395 6811.4495 8744.180 100
# akrun 238.893 304.3680 366.0376 327.7265 416.3815 654.744 100
# p_routh 1013.967 1190.9255 1418.8037 1296.7450 1651.7220 2162.775 100
# d.b 133.072 183.8595 228.7220 207.0415 278.1780 417.974 100
举个更大的例子:
140E6比基准测试要高一点,所以我尝试使用1E5。我任意选择a和b之间大约50%的重叠
n <- 1E5
set.seed(1)
a <- sample(2*n,n)
b <- sample(2*n,n)
我们发现p Routh的解对于2个向量是最快的,dcast
是最快的一般解。merge
withdata。然而,对于140E6行,table
可能是最快的
通用解决方案
有希望的最终编辑:
我根据我最好的受限解决方案设计了2个通用解决方案,并在3个大小为10E6的向量上运行它们
merge_dt_gen <- function(...){
args <- as.character(substitute(list(...)))[-1]
dts <- args %>% lapply(.%>% data.table(ip=get(.),key="ip"))
all_ips <- data.table(ip = unique(c(...)),key="ip") # all_ips <- data.table(ip = unique(c(a,b)))
for(dt in dts){
all_ips <- merge(all_ips,dt,all.x = TRUE,by="ip")
}
all_ips %>%
as.data.frame %>%
set_rownames(.,`[`(.,,'ip')) %>%
select(-ip) %>%
setNames(args) %>%
replace(.,!is.na(.),1) %>%
replace(.,is.na(.),0)
}
d_cast_gen <- function(...){
args <- as.character(substitute(list(...)))[-1]
args %>%
lapply(.%>% data.frame(get(.)) %>% setNames(c("src","ip"))) %>%
do.call(rbind,.) %>%
transform(v=1) %>%
dcast(ip ~ src,value.var="v") %>%
replace(.,is.na(.),0) %>%
setNames(gsub("v","",colnames(.))) %>%
set_rownames(.,`[`(.,,'ip')) %>% select(-ip)
}
n <- 10E6
set.seed(1)
a <- sample(2*n,n)
b <- sample(2*n,n)
d <- sample(unique(a,b),n)
microbenchmark(
d_cast_gen = d_cast_gen(a,b,d),
merge_dt_gen = merge_dt_gen(a,b,d),
times = 1
)
# Unit: seconds
# expr min lq mean median uq max neval
# d_cast_gen 70.99771 70.99771 70.99771 70.99771 70.99771 70.99771 1
# merge_dt_gen 47.41809 47.41809 47.41809 47.41809 47.41809 47.41809 1
merge\u dt\u gen%
集合名(参数)%%>%
替换(,!is.na(.),1)%>%
替换(,is.na(.),0)
}
d_cast_gen%data.frame(get(.))%%>%setNames(c(“src”,“ip”))%%>%
do.呼叫(rbind),%%>%
变换(v=1)%>%
dcast(ip~src,value.var=“v”)%%>%
替换(,is.na(.),0)%>%
集合名(gsub(“v”),“”,colnames(.))%>%
设置行名(,`[`,,'ip'))%>%select(-ip)
}
n我们可以通过将“a”、“b”转换为因子来实现这一点,其中级别指定为组合“a”、“b”中的唯一元素,并获得频率
lvl <- unique(c(a,b))
mapply(table, list(a = factor(a, levels = lvl),b = factor(b, levels = lvl)))
# a b
#192.168.0.1 1 0
#192.168.0.2 1 0
#192.168.0.3 1 0
#192.168.0.4 1 0
#192.168.0.5 1 1
#192.168.0.6 1 1
#192.168.0.7 1 1
#192.168.0.8 1 1
#192.168.0.9 1 1
#192.168.0.10 1 1
#192.168.1.1 0 1
#192.168.1.2 0 1
#192.168.1.3 0 1
#192.168.1.4 0 1
lvl我们可以通过将“a”、“b”转换为因子来实现这一点,其中级别指定为组合“a”、“b”中的唯一元素,并获得频率
lvl <- unique(c(a,b))
mapply(table, list(a = factor(a, levels = lvl),b = factor(b, levels = lvl)))
# a b
#192.168.0.1 1 0
#192.168.0.2 1 0
#192.168.0.3 1 0
#192.168.0.4 1 0
#192.168.0.5 1 1
#192.168.0.6 1 1
#192.168.0.7 1 1
#192.168.0.8 1 1
#192.168.0.9 1 1
#192.168.0.10 1 1
#192.168.1.1 0 1
#192.168.1.2 0 1
#192.168.1.3 0 1
#192.168.1.4 0 1
lvl不知道为什么,但是我用这个得到的结果与其他的不同:[1]“dplyr a匹配是:11999”[1]“dplyr b匹配是:6179”[1]“sapply a匹配是:11999”[1]“sapply b匹配是:6179”[1]“mapply a匹配是:10998”[1]“mapply b匹配是:3001”@角色不确定。你有NA
值吗那里不应该有任何NAs。我会调查一下,看看是否能找出它的不同之处。不:>length(mappy_-df[is.NA(mappy_-df)])[1]0我会继续查找。不确定为什么,但这一个与其他的结果不同:[1]“dplyr a匹配为:11999”[1]“dplyr b匹配为:6179”[1]“sapply a匹配为:11999”[1]“sapply b匹配为:6179”[1]“mapply a匹配为:10998”[1]”mapply b匹配为:3001“@TheProletariat对此不确定。你有NA
值吗那里不应该有任何NAs。我会调查一下,看看是否能找出它的不同之处。不:>长度(mappy_-df[is.NA(mappy_-df)])[1]0我会继续查找。