如何在Mathematica中基于部分字符串匹配进行选择

4

假设我有一个矩阵,它看起来像这样:

{{foobar, 77},{faabar, 81},{foobur, 22},{faabaa, 8},
{faabian, 88},{foobar, 27}, {fiijii, 52}}

以及像这样的列表:

{foo, faa}

现在我想根据列表中字符串的部分匹配,对矩阵中每行的数字进行加总,以便得到以下结果:
{{foo, 126},{faa, 177}}

我认为我需要映射一个Select命令,但我不太确定如何做到这一点,并且只匹配部分字符串。有人可以帮助我吗?现在我的真实矩阵大约有150万行,因此一些不太慢的东西会增加价值。

4个回答

3

这里是一个起点:

data={{"foobar",77},{"faabar",81},{"foobur",22},{"faabaa",8},{"faabian",88},{"foobar",27},{"fiijii",52}};

{str,vals}=Transpose[data];
vals=Developer`ToPackedArray[vals];
findValPos[str_List,strPat_String]:=
    Flatten[Developer`ToPackedArray[
         Position[StringPosition[str,strPat],Except[{}],{1},Heads->False]]]

Total[vals[[findValPos[str,"faa"]]]]

2

这里有另一种方法。它速度较快且简洁明了。

data =
 {{"foobar", 77},
  {"faabar", 81},
  {"foobur", 22},
  {"faabaa", 8},
  {"faabian", 88},
  {"foobar", 27},
  {"fiijii", 52}};

match = {"foo", "faa"};

f = {#2, Tr @ Pick[#[[All, 2]], StringMatchQ[#[[All, 1]], #2 <> "*"]]} &;

f[data, #]& /@ match
{{"foo", 126}, {"faa", 177}}

您可以使用ruebenko的预处理方法来提高速度。
在我的系统上,这种方法大约比他的方法快两倍:

{str, vals} = Transpose[data];
vals = Developer`ToPackedArray[vals];

f2 = {#, Tr @ Pick[vals, StringMatchQ[str, "*" <> # <> "*"]]} &;

f2 /@ match

请注意,在这个版本中,我测试了不在开头的子字符串,以匹配ruebenko的输出。如果你只想匹配字符串的开头,这也是我在第一个函数中假设的,那么速度会更快。


我喜欢它。快速、灵活,完全满足我的需求。 - Mr Alpha

1

生成数据

mat = {{"foobar", 77},
   {"faabar", 81},
   {"foobur", 22},
   {"faabaa", 8},
   {"faabian", 88},
   {"foobar", 27},
   {"fiijii", 52}};
lst = {"foo", "faa"};

现在选择

r1 = Select[mat, StringMatchQ[lst[[1]], StringTake[#[[1]], 3]] &];
r2 = Select[mat, StringMatchQ[lst[[2]], StringTake[#[[1]], 3]] &];
{{lst[[1]], Total@r1[[All, 2]]}, {lst[[2]], Total@r2[[All, 2]]}}

提供

{{"foo", 126}, {"faa", 177}}

如果可以的话,我会尽力使它更加通用和实用...

编辑(1)

以下内容使其更加通用(使用与上述相同的数据):

foo[mat_, lst_] := Select[mat, StringMatchQ[lst, StringTake[#[[1]], 3]] &]
r = Map[foo[mat, #] &, lst];
MapThread[ {#1, Total[#2[[All, 2]]]} &, {lst, r}]

提供

{{"foo", 126}, {"faa", 177}}

所以,如果lst被更改为3个项目,上面的相同代码现在将起作用:

lst = {"foo", "faa", "fii"};

1

这样怎么样:

list = {{"foobar", 77}, {"faabar", 81}, {"foobur", 22}, {"faabaa", 
    8}, {"faabian", 88}, {"foobar", 27}, {"fiijii", 52}};

t = StringTake[#[[1]], 3] &;

{t[#[[1]]], Total[#[[All, 2]]]} & /@ SplitBy[SortBy[list, t], t]

{{"faa", 177}, {"fii", 52}, {"foo", 126}}

我确信我曾经读过一篇文章,可能是在这里,在那篇文章中有人描述了一种有效地结合排序和拆分的函数,但我记不起来了。如果有人知道它的话,也许可以添加评论。

编辑

好吧,该睡觉了--我怎么能忘记 Gatherby 呢。

{t[#[[1]]], Total[#[[All, 2]]]} & /@ GatherBy[list, t]

{{"foo", 126}, {"faa", 177}, {"fii", 52}}

请注意,对于一个包含1.4百万个对的虚拟列表,这需要几秒钟的时间,因此并不是一个非常快速的方法。

刚刚注意到在基于GatherBy的答案发布后,你要求了一个评论,并且你在睡前记起了它。删除我的答案... 对于一个小的泛化,你可以让t接受一个参数t[k_]=StringTake[@[[1]],k]%。(+1) - kglr

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接