R 如何从两个具有公共值的列表中创建虚拟矩阵?

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","

在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","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
with
data。然而,对于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我会继续查找。