如何在矩阵中插入一列,正确的 Mathematica 方法

26

我认为Mathematica更倾向于行而非列。

给定一个矩阵,在其中插入一行似乎很容易,只需使用Insert[]

(a = {{1, 2, 3}, {4, 0, 8}, {7 , 8, 0}}) // MatrixForm

1   2    3
4   0    8
7   8    0

 row = {97, 98, 99};
(newa = Insert[a, row, 2]) // MatrixForm

1   2   3
97  98  99
4   0   8
7   8   0

但是,经过一番努力,我找到了两种方法来插入列,我在下面展示,并希望在这里问问专家们是否看到更短、更直接的方式(Mathematica有很多命令,我可能忽略了一个更简单的做法),因为我认为现在的方法对于这样一个基本操作来说还是太复杂了。

第一种方法

必须进行双重转置:

a = {{1, 2, 3}, {4, 0, 8}, {7 , 8, 0}}
column = {97, 98, 99}
newa = Transpose[Insert[Transpose[a], column, 2]]

1   97  2   3
4   98  0   8
7   99  8   0

第二种方法

使用SparseArray,但需要注意索引位置。有点笨拙,用于执行以下操作:

(SparseArray[{{i_, j_} :> column[[i]] /; j == 2, {i_, j_} :> a[[i, j]] /; j == 1, 
              {i_, j_} :> a[[i, j - 1]] /; j > 1}, {3, 4}]) // Normal

1   97  2   3
4   98  0   8
7   99  8   0
问题是:是否有一种更加函数化的方式,比上面的方法更短?当然我可以使用上述之一,并用一个函数将整个过程包装起来,例如insertColumn[...],以便于使用。但我想知道是否有比我现在做的更简单的方法。
供参考,以下是我在Matlab中执行此操作的方式:
EDU>> A=[1 2 3;4 0 8;7 8 0]
A =
     1     2     3
     4     0     8
     7     8     0

EDU>> column=[97 98 99]';

EDU>> B=[A(:,1) column A(:,2:end)]

B =
     1    97     2     3
     4    98     0     8
     7    99     8     0

2
重复问题:插入到二维列表中 - WReach
@WReach,您链接的问题有点狭窄,那里的请求是在末尾添加列(尽管那里的一些解决方案更为通用,也适用于此问题)。 - Leonid Shifrin
2
这个问题也很相似。Janus指出了通过以下ArrayFlatten方法插入一列零(例如)的“技巧”:ArrayFlatten@{{a[[All, ;; 1]], 0, a[[All, 2 ;; 3]]}}(在我看来,一个最近的问题被视为“可能的重复项”,受到了比这个问题更严厉的对待)。 - 681234
@TomD 我认为问题没有得到同等对待的一个原因是更多的人意识到之前的问题是重复的(例如,我不知道这个问题的主题在类似的环境中被讨论过,Sjoerd也肯定不知道)。这是因为很多人(包括我自己)最近才加入了mma SO标签。另一方面,一旦问题获得动力(答案、投票、评论等),关闭可能不再是一个好选择。 - Leonid Shifrin
5个回答

19

你的双重Transpose方法看起来很好。对于非常大的矩阵,这将快2-3倍:

MapThread[Insert, {a, column, Table[2, {Length[column]}]}]

如果你想模仿Matlab的方式,最接近的可能是这样:

ArrayFlatten[{{a[[All, ;; 1]], Transpose[{column}], a[[All, 2 ;;]]}}]

请记住,插入需要制作矩阵的完整副本。因此,如果您计划以这种方式构建矩阵,则最好预分配矩阵(如果您知道其大小),并通过 Part 进行原地修改,以提高效率。


1
谢谢,我离你第二种方法有点接近了,但我写了a[[All,1]],这使得从那里开始变得困难,直到现在才知道我可以使用a[[All,;; 1]]。这个小差别使得更容易使用输出。真的很有帮助知道这个酷炫的技巧。我相信当我在Mathematica中经常使用矩阵时,这会很方便。再次感谢。 - Nasser
1
我喜欢第一种方法,只需进行小修改:MapThread[Insert[#1, #2, j] &, {a, column_vector}] - Say OL

11
你可以使用级别规定为2的Join函数,以及将元素大小设置为1的Partition函数进行子集分割:
a = {{1, 2, 3}, {4, 0, 8}, {7 , 8, 0}}
column = {97, 98, 99}
newa = Join[a,Partition[column,1],2]

我认为这是提议解决方案的精简版。非常有效! - Soid

7

我认为我会以同样的方式去做,但以下是其他的一些方式:

-使用 MapIndexed

newa = MapIndexed[Insert[#1, column[[#2[[1]]]], 2] &, a]

-使用Sequence
newa = a;
newa[[All, 1]] = Transpose[{newa[[All, 1]], column}];
newa = Replace[a, List -> Sequence, {3}, Heads -> True]

有趣的是,这似乎是一种可以“原地”工作的方法,即它不需要像Leonid的答案中所述那样进行矩阵复制,如果您打印结果矩阵,它显然很有效。
然而,有一个很大的问题。请参见mathgroup讨论“part assigned sequence behavior puzzling”中关于Sequence的问题。

3
你提到的陷阱是我不使用基于Sequence的方法的原因之一。这里有两个相关链接,其中一个非常新:http://groups.google.com/group/comp.soft-sys.math.mathematica/browse_thread/thread/b7303eafccee6d5a,和http://groups.google.com/group/comp.soft-sys.math.mathematica/browse_thread/thread/8d04685fd7283130。我通常对基于`Sequence`的赋值持有负面态度,原因在我在那些线程中的帖子中提到。不使用`Sequence`的另一个原因是这将导致打包矩阵的解包。 - Leonid Shifrin
3
顺便提一下,使用“Sequence”方法仍然会导致复制,只是更加隐蔽。当使用“Insert”时,复制在定义时完成。而使用基于“Sequence”的方法,在运行时,在评估的“Sequence”拼接步骤中进行复制。在这方面,我认为MathLink文档中关于使用“Sequence”构建列表的示例特别具有误导性,您可以在这里查看:http://reference.wolfram.com/mathematica/tutorial/HandlingListsArraysAndOtherExpressions.html 特别是他们强调性能,但忽略了这种方法具有二次复杂度(像“Append”一样)。 - Leonid Shifrin

4
我通常会这样做:

我通常只是这样做:

In: m0 = ConstantArray[0, {3, 4}]; 
    m0[[All, {1, 3, 4}]] = {{1, 2, 3}, {4, 0, 8}, {7, 8, 0}}; 
    m0[[All, 2]] = {97, 98, 99}; m0 

Out: 
    {{1, 97, 2, 3}, {4, 98, 0, 8}, {7, 99, 8, 0}}

我不知道它在效率方面如何比较。


4

我最初将此作为评论(现已删除)发布

基于user656058this问题(Mathematica 'Append To' Function Problem)中提供的一种方法和Mr Wizard回复,可以获得以下添加矩阵列的替代方法,使用TableInsert

(a = {{1, 2, 3}, {4, 0, 8}, {7, 8, 0}});
column = {97, 98, 99};

Table[Insert[a[[i]], column[[i]], 2], {i, 3}] // MatrixForm

给予

enter image description here

同样地,要添加一列零(例如):
Table[Insert[#[[i]], 0, 2], {i, Dimensions[#][[1]]}] & @ a 

如上方评论所述,Janus 指出了通过 ArrayFlatten 方法添加零列的 "技巧"(请参见 这里)。
ArrayFlatten[{{Take[#, All, 1], 0, Take[#, All, -2]}}] & @ 
  a // MatrixForm

编辑

也许对于较小的矩阵来说更简单。

(Insert[a[[#]], column[[#]], 2] & /@ Range[3]) // MatrixForm

或者,插入一列零
Insert[a[[#]], 0, 2] & /@ Range[3]

或者,稍微通俗地说:

Flatten@Insert[a[[#]], {0, 0}, 2] & /@ Range[3] // MatrixForm

当然,也可以很容易地适应于AppendPrepend的工作。

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