【问题标题】:Conditional Data Manipulation in MathematicaMathematica 中的条件数据操作
【发布时间】:2011-09-02 02:00:15
【问题描述】:

我正在尝试在 Mathematica 中准备 用于高效数据分析的最佳工具。 我有大约 300 列和 100 000 行。

什么是最好的技巧:

“删除”、“提取”或简单地“考虑”数据结构的部分,例如用于绘图

我能想到的最棘手的例子之一是:

给定一个数据结构,

为第 2 列中的值等于 x 而第 8 列中的值不同于 y 的每一行提取第 1 列到第 3 列、第 6 列到第 9 列以及最后一个列

我也欢迎任何有关数据操作的一般性建议。

【问题讨论】:

  • 天哪!我可能会在这里被扔石头,但我能给你的最好的建议是使用 MATLAB 对数值数据结构进行上述所有操作:D
  • 我很高兴我离开了 Matlab ! Mathematica 的界面对我来说是天堂 ;)
  • 当您的问题是 mma 时,我并不是真的建议您切换到 MATLAB :)
  • @yoda 我们真的需要一个讽刺符号,就像问号和引号一样。我真的以为你是认真的。
  • @Sjoerd:当你问我如何索引矩阵时,我通常会保留‽,但我给你的是烤兔子。

标签: data-structures wolfram-mathematica


【解决方案1】:

我读到了:

为第 2 列中的值等于 x 且第 8 列中的值不同于 y 的每一行提取第 1 列到第 3 列、第 6 列到第 9 列以及最后一个列

作为我们想要的意思:

  • 每行的元素 1-3 和 6-9

  • 行中的最后一个元素,其中[[2]] == x && [[8]] != y

这是我一起破解的:

a = RandomInteger[5, {20, 10}];          (*define the array*)
x = 4; y = 0;                            (*define the test values*)

Join @@ Range @@@ {1 ;; 3, 6 ;; 9};      (*define the column ranges*)

#2 == x && #8 != y & @@@ a;              (*test the rows*)

Append[%%, #] & /@ % /. {True -> -1, False :> Sequence[]};  (*complete the ranges according to the test*)

MapThread[Part, {a, %}] // TableForm     (*extract and display*)

【讨论】:

    【解决方案2】:

    对于具有命名列的表中的数据的通用操作,我建议您参考我的this 解决方案,以解决类似的问题。对于任何特定情况,手动为Select 编写函数可能更容易。但是,对于许多列和许多不同的查询,弄乱索引的机会很高。这是来自上述帖子的修改后的解决方案,它提供了更友好的语法:

    Clear[getIds];
    getIds[table : {colNames_List, rows__List}] := {rows}[[All, 1]];
    
    ClearAll[select, where];
    SetAttributes[where, HoldAll];
    select[cnames_List, from[table : {colNames_List, rows__List}], where[condition_]] :=
    With[{colRules =  Dispatch[ Thread[colNames -> Thread[Slot[Range[Length[colNames]]]]]],
        indexRules  =  Dispatch[Thread[colNames -> Range[Length[colNames]]]]},
         With[{selF = Apply[Function, Hold[condition] /. colRules]},
           Select[{rows}, selF @@ # &][[All, cnames /. indexRules]]]];
    

    这里发生的是Select 中使用的函数是根据您的规范自动生成的。例如(使用@Yoda 的例子):

    rows = Array[#1 #2 &, {5, 15}];
    

    我们需要定义列名(必须是不带值的字符串或符号):

    In[425]:= 
    colnames = "c" <> ToString[#] & /@ Range[15]
    
    Out[425]= {"c1", "c2", "c3", "c4", "c5", "c6", "c7", "c8", "c9", "c10", "c11", "c12", 
    "c13", "c14", "c15"}
    

    (在实践中,通常名称当然更具描述性)。这是表格:

    table = Prepend[rows, colnames];
    

    这是您需要的选择语句(我选择了x = 4y=2):

    select[{"c1", "c2", "c3", "c6", "c7", "c8", "c9", "c15"}, from[table],
        where["c2" == 4 && "c8" != 2]]
    
    {{2, 4, 6, 12, 14, 16, 18, 30}}
    

    现在,对于单个查询,这可能看起来是一种复杂的方法。但是你可以做很多不同的查询,比如

    In[468]:= select[{"c1", "c2", "c3"}, from[table], where[EvenQ["c2"] && "c10" > 10]]
    
    Out[468]= {{2, 4, 6}, {3, 6, 9}, {4, 8, 12}, {5, 10, 15}}
    

    和类似的。

    当然,如果您的数据中存在特定的相关性,您可能会找到一种更快的特定专用算法。上面的函数可以通过多种方式进行扩展,以简化常见查询(包括“all”等),或自动编译生成的纯函数(如果可能)。

    编辑

    在哲学上,我相信许多 Mathematica 用户(包括我自己)发现自己不时地一次又一次地编写类似的代码。 Mathematica 具有简洁的语法这一事实使得它通常很容易为任何特定情况编写。但是,只要一个人在某个特定领域工作(例如,表中的数据操作),对于许多操作来说,重复自己的成本就会很高。我的示例在一个非常简单的设置中说明了一种可能的出路 - 创建一种特定于域的语言 (DSL)。为此,通常需要为它定义一个语法/语法,并从它编写一个编译器到 Mathematica(以自动生成 Mathematica 代码)。现在,上面的例子是这个想法的一个非常原始的实现,但我的观点是,Mathematica 通常非常适合 DSL 创建,我认为这是一种非常强大的技术。

    【讨论】:

    • @Leonid,您的解决方案似乎可以处理字符串列标题。一般来说,您会建议在数据结构中保留由字符串组成的标题吗?另外我必须承认,我在使用您的代码时有些吃力,但我仍在努力尝试找出我做错了什么。如果我失败了,我会尝试以最好的方式来表达问题,以寻求您的帮助。再次感谢!
    • @500 在表数据操作的特殊情况下,我建议保留标题只是因为它们更具描述性,并且您获得列索引错误的机会更少 - 我的解决方案应该是自动转换为索引号。至于我的代码,让我知道哪些部分可能会导致混淆,我会发布更好的解释。可能具有指导意义的一件事是查看为Select 生成的纯函数 - 例如,您可以使用我在最近关于调试的问题中描述的调试实用程序ShowIt 来做到这一点。
    • @500 保留标题的另一个很好的理由是它们提供了一定程度的间接性并使您的解决方案更加健壮:如果您希望稍后更改表中列的顺序(例如,通过在表中插入一些新的列类型,或者删除现有的列类型,或者只是交换列),使用标题名称进行的查询将保持有效,而所有具有显式索引的函数都可能成为垃圾。出于同样的原因,真正的 SQL 查询使用列名而不是数字索引进行操作。
    • @Leonid,你介意看看这个笔记本,当我尝试使用你的代码时,你会看到发生了什么。我想我真的错过了一些愚蠢的东西,如果是这样,请原谅我。再次感谢您的帮助:laeh500.com/LAEH/For_Leonid.html
    • @500 为我工作。对于select[{"RowNO", "trialNo"}, from[table], where["condCOG" == 2]],我会为您的桌子获取{{1, 1}, {4, 2}, {8, 4}, {14, 7}, {16, 8}} 列表。我认为这是正确的。如果您还有其他问题,也可以在 gmail dot com 的 lshifr 上给我写信,您可以在其中将文件作为附件发送。那么,究竟什么对你不起作用?你得到了相同的结果列表吗?
    【解决方案3】:

    要提取列(或行),您可以通过部分索引来完成

    data = Array[#1 #2 &, {5, 15}];
    data[[All, Flatten@{Range@3, Range @@ {6, 9}, -1}]]
    
    MatrixForm@%
    

    最后一行只是为了美观。

    正如 Sjoerd 在他的评论中提到的(以及在他的回答中的解释中),可以使用 Span (;;) 命令轻松完成对单个范围的索引。如果您要连接多个不相交的范围,使用Flatten 组合使用Range 创建的单独范围比手动输入它们更容易。

    【讨论】:

    • 也许应该指出 Range 在这里很有用,因为我们有几个你使用 Flatten 组合的分离范围。否则可能会使用 Span (;;)。
    【解决方案4】:
    data = RandomInteger[{1, 20}, {40, 20}]
    
    x = 5;
    y = 8;
    Select[data, (#[[2]] == x && #[[8]] != y &)][[All, {1, 2, 3, 6, 7, 8, 9, -1}]]
    
    ==> {{5, 5, 1, 4, 18, 6, 3, 5}, {10, 5, 15, 3, 15, 14, 2, 5}, {18, 5, 6, 7, 7, 19, 14, 6}}
    

    获取矩阵和列表的一些有用命令是Span (;;)、DropTakeSelectCases 等。见tutorial/GettingAndSettingPiecesOfMatricesguide/PartsOfMatrices

    Part ([[...]]) 与;; 结合使用会非常强大。例如,a[[All, 1;;-1;;2]] 表示取所有行和所有奇数列(-1 具有从末尾开始计数的通常含义)。

    Select 可用于根据逻辑函数从列表中选择元素(记住矩阵是列表的列表)。它的孪生兄弟是Cases,它根据模式进行选择。我在这里使用的函数是'pure' function,其中# 指的是应用此函数的参数(在这种情况下是列表的元素)。由于元素本身是列​​表(矩阵的行),我可以使用 Part ([[..]]) 函数来引用列。

    【讨论】:

    • @Sjoerd:我认为这两点是不相交的。我认为@500 的意思是 1. 如何挑选列和 2. 在第 2 列和第 8 列中查找元素.. yada yada。无论如何,这是对上述解决方案的简单修改。
    • @yoda 我不这么认为。这两个要求读作一个句子,我将它们解释为需要一起满足的两个要求。你为什么删除你的答案? Range 部分是我没有的,当您想要选择的列超过此处所需的几列时,它可能会派上用场。
    • @500 @yoda 'Single Sentence it was' 那是尤达谈话 ;-)
    • @Sjoerd 你给我的关于矩阵的参考是否适用于列表?所有列表? “矩形”列表?列表//MatrixForm ?
    • @Sjoerd:我删除了,因为你的更完整,我误读了要求。无论如何,现在未删除,我已将其更改为仅解决拉出列的问题。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2023-01-20
    • 1970-01-01
    • 2013-01-12
    • 2011-07-11
    • 1970-01-01
    • 2021-12-22
    相关资源
    最近更新 更多