bounty 15小时后到期。回答此问题可获得+300声望奖励。tmfmnk希望引起更多的注意这个问题。
我有一个大型数据集,其中包含40多年来不规则采样的站点。我想选择共享的网站的最大数量,比方说,至少5年的数据。
任何指针将不胜感激。
下面是一个示例数据集:
library(tidyverse)
set.seed(123)
(DF <- tibble(
Sites = 1:100,
NYears =rbinom(100, 40, .2)
) %>%
rowwise() %>%
mutate(Years = list(sample(1982:2021, NYears))) %>%
unnest(Years) %>%
select(-NYears))
字符串
6条答案
按热度按时间6ljaweal1#
免责声明
下面的方法 * 不 * 像solution by @jblood94一样有效(所以,如果你追求速度,不要使用我的解决方案来处理大型数据集),但是***只是为了改变思维方式,以图论的方式思考,并探索使用
igraph
解决问题的可能性***。Brief Idea(图论视角)
一般来说,我认为这个问题可以用图论的方法来处理,用
igraph
来解决。如果你追求效率,你可能需要探索隐藏在图形背后的潜在属性。举例来说:Years
的数量可以解释为与两个Sites
顶点相关联的边权重。<=4
的一些边,因此可以进一步简化图。修剪网络并在之后进行搜索应该比迭代所有可能的组合更有效。如果您对细节感兴趣,请参考code breakdowns的后续回答。
一种
igraph
方式下面可能是一个
igraph
选项来解决这个问题(有关详细信息,请参阅代码的注解):您可以在Sites
上尝试graph_from_adjacency_matrix
,并使用cliques()
查找集团,例如,字符串
或替代方案
型
这给了
型
如果您想进一步显示共享年份,可以在
res
之上执行其他操作,例如,型
这给了
型
讨论
在上面的
igraph
选项中,cliques()
将是性能瓶颈,特别是当条件是“共享Years
的数量应该是>=k
”时,对于小型k
,例如k=1
或k=2
。在这些情况下,cliques()
在Filter()
之前枚举的集团明显更多。您可以参考@jblood94的基准测试结果。qgelzfjb2#
这里是一种方法,从满足标准的站点对开始,然后迭代地将站点添加到每个组。它非常高效,几乎立即解决了OP数据集上的5个共享年问题。
几乎可以肯定的是,这种方法的效率还有待提高,但这应该给予一个很好的起点。
作为一个函数实现:
字符串
测试:
型
标杆管理
基准测试将把这种方法与@ThomasIsCoding的
igraph
解决方案进行比较。但是,当为最小共享年数选择较小的值(OP的数据集为1或2)时,igraph
会变得非常慢。这些被排除在基准之外。igraph
解决方案作为函数:型
时间:
型
igraph
方法始终较慢,有时甚至非常慢。数据
型
2vuwiymt3#
这是一个简单的解决方案,虽然它可能对您的大型数据集不可行,但可能是寻找更好解决方案的良好起点:
字符串
jum4pzuy4#
这里是我最初的蛮力方法的一个(有点)优化版本,它现在不会遍历所有可能的年份组合,而是迭代地构建在 n+1年内采样的网站列表。
字符串
仍然需要大约一秒钟的时间来计算,因此其他答案可能更有效,但与蛮力方法相比,显着减少了访问的组合数量。
获奖者是:
型
原始答案
型
代码很短,但背后有大量的计算。
yr.sites
是一个列表,存储每年采样的站点。combn
线生成长度为5年的所有组合(有658008个这样的组合),并为每个组合找到在所有这些年中采样的站点。最后,有2组最多三个站点满足初始条件。我们可以用
型
这表明,例如第一组中的所有三个地点实际上都是在1989年、1991年、1998年、2001年和2011年取样的。
正如我已经说过的,这个解决方案是无效的,如果你想要求超过5年的共享,它将无法很好地扩展。例如,在6年的情况下,组合(
choose(40, 6)
)的数量将增加到3838380,并且更高的年数也不是好消息。nnt7mjpx5#
igraph
solution后续:分解代码我不想让我的
igraph
答案太长而难以阅读,所以我创建了一个新的答案来详细说明该解决方案的工作原理。虚拟示例
为了简单起见,我们可以从一个较小的虚拟示例开始,我们假设我们的目标是 * 找到共享至少
3
年 * 的Sites
的最大数量。字符串
并且
DF
在图中的可视化看起来像可以通过型
的数据
步骤
Sites
和Years
是两种不同类型的顶点),并且共享的Years
实际上可以投影到边。由于我们需要跟踪共享Years
的 * 数量 *,因此可以使用边属性"weight"
来标识共享Years
的计数。在这种情况下,在投影之前需要Sites
的邻接矩阵,并且通过table
+tcrossprod
来获得Sites
,例如,型
这给了
型
>=3
共享年的组,这意味着边的"weight"
至少应为3
。在步骤1)中获得的邻接矩阵adjmat
之上,我们可以进一步应用过滤器(>=3
)来简化矩阵,这相当于 * 修剪 * 网络,即,型
而
plot(g)
显示的项目如下所示Years
的Sites
应该产生一个完全子图,即 * 团 *。因此,我们由此可以枚举图g
中的所有团,这由函数cliques()
完成,即,型
其中
min = 2
指定每个团的最小大小。如果您事先没有关于大小的信息,使用cliques()
也可以,不需要任何额外的参数。现在,clq
是一个列表,看起来像**4).需要注意的是,并非所有的集团都满足共享
Years
的>=3
要求,因为 “权重” 表示的是总共享Years
的计数,而不是区分共享Years
的计数。换句话说,2000, 2001, 2002
是有效的,但2000, 2000, 2002
不是,尽管后者的计数为3
。因此,我们需要检查每个团中共享Years
的分布。例如,如果我们查看第三个团,即
clq[[3]]
,并检查共享Years
的分布我们看到所有
1
的列是2009
和2011
,这意味着它们只有 * 2 * 共享Years
,因此无效。为了挑选有效的集团,我们可以使用Filter
和过滤标准,例如,等着瞧吧
其中89个团中的32个满足要求。
validclq
中选择具有最大大小的团(因为我们正在寻找Sites
的 * 最大 * 数量)。使用lenghts
来获得每个团的大小,我们可以过滤具有最大大小的团,例如,我们终于得到了
mtb9vblg6#
这里有两个使用动态规划技术的基本R选项,它们通过一棵“树”搜索所需的输出,当添加新的可行
Sites
时,“树”可以扩展。Base R实现
checker
从矩阵中检索行,并检查共享年份数是否满足要求字符串
ftic2
,其中我们应用掩码msk
来提升checker
,因为它只需要当前掩码和新行进行检查(* 因为colSums
一次又一次地使用冗余信息,这导致效率低下 *)。因此,ftic2
应该比ftic1
性能更好,尽管它看起来不那么整洁!型
输出示例
型
正如我们所看到的,在
ftic2
的结果中,msk
字段指示哪些Years
由idx
字段提供的Sites
索引共享。从这个意义上说,当您打算获取共享Years
的信息时,ftic2
比ftic1
更方便。对标
在这里,我们将@jblood94的解决方案作为基线,因为该解决方案是迄今为止最有效的。
>=3
型
>=4
型
>=5
型
>=6