作为其他解决方案的补充,我想探讨一下这个问题的高性能角落,即表很大并且需要执行许多查询的情况。显然,在这种情况下,某种预处理可以节省大量的执行时间。我想展示一个基于Dispatch
和组合的相当晦涩但 IMO 优雅的解决方案ReplaceList
。这是一个用于说明的小表格(我对所有条目都使用字符串,以使其接近原始问题):
makeTestTable[nids_, nelems_] :=
Flatten[Thread[{"ID" <> ToString@#,
ToString /@ Range[#, nelems + # - 1]}] & /@ Range[nids], 1]
In[57]:= (smallTable = makeTestTable[3,5])//InputForm
Out[57]//InputForm=
{{"ID1", "1"}, {"ID1", "2"}, {"ID1", "3"}, {"ID1", "4"}, {"ID1", "5"},
{"ID2", "2"}, {"ID2", "3"}, {"ID2", "4"}, {"ID2", "5"}, {"ID2", "6"},
{"ID3", "3"}, {"ID3", "4"}, {"ID3", "5"}, {"ID3", "6"}, {"ID3", "7"}}
预处理步骤包括Dispatch
从原始表制作一个规则表:
smallRules = Dispatch[Rule @@@ smallTable];
获取(例如,对于“ID2”)值的代码是:
In[59]:= ReplaceList["ID2", smallRules]
Out[59]= {"2", "3", "4", "5", "6"}
这看起来没什么大不了,但让我们转向更大的表:
In[60]:= Length[table = makeTestTable[1000,1000]]
Out[60]= 1000000
诚然,预处理步骤需要一些时间:
In[61]:= (rules = Dispatch[Rule @@@ table]); // Timing
Out[61]= {3.703, Null}
但我们只需要一次。现在,所有后续查询(可能除了第一个查询)都将接近即时:
In[75]:= ReplaceList["ID520",rules]//Short//Timing
Out[75]= {0.,{520,521,522,523,524,525,<<988>>,1514,1515,1516,1517,1518,1519}}
而没有预处理的方法对于这个表大小需要相当大的几分之一秒:
In[76]:= Cases[table,{"ID520",_}][[All,2]]//Short//Timing
Out[76]= {0.188,{520,521,522,523,524,525,<<988>>,1514,1515,1516,1517,1518,1519}}
我意识到这对于原始问题来说可能有点过头了,但是这样的任务相当普遍,例如,当有人想直接在 Mathematica 中探索从数据库导入的一些大型数据集时。