12
$\begingroup$

If I have a table of data and I want to do a specific operation on say the third column, I usually do something like this:

OperatedOnTable = 
  MyTable /. {Col1_, Col2_, Col3_, Col4_} -> {Col1, Col2, 2*Pi*Col3 + 42, Col4}

where MyTable has four columns in this case.

This is fine when I have a table with several columns, but when I have a table that has, say, twenty columns, this quickly becomes unwieldy and silly, as I need to map every single column such that I may pick out the columns I want to operate on.

For tables with large numbers of columns, how can I perform an operation on a specific column or column?


I should add, it would be preferable to be able to perform operations on multiple columns as is possible with my example, e.g.

OperatedOnTable = 
  MyTable /. 
    {Col1_, Col2_, Col3_, Col4_} -> {Col1 + 1/137, Col2, 2*Pi*Col3 + 42, Col4}

Note that I have performed two distinct operations on two distinct columns with a single command. I'd like to be able to replicate this functionality.


I want to achieve the same functionality as:

OperatedOnTable = 
  MyTable /. 
    {Col1_, Col2_, Col3_, Col4_, Col5_, Col6_, Col7_, Col7_, Col8_, 
     Col9_, Col10_, Col11_, Col12_, Col13_, Col14_, Col15_, Col16_} -> 
    {Col1 + 1/137, Col2, 2*Pi*Col3 + 42, Col4, Col5, Col6, Col7, Col7, Col8, 
     Col9, Col10, Col11, Col12, Col13, Col14, Col15, Col16}

This achieves what I want, manipulation of multiple columns in a single operation, but as we can see it is somewhat silly for tables with tens of columns.

I'm using Mathematica 12.1.1.0.

$\endgroup$
5
  • 2
    $\begingroup$ MyTable = MapAt[2*Pi*#+ 42&, MyTable, {All, 3}]? $\endgroup$
    – kglr
    Commented Sep 27, 2020 at 20:18
  • 1
    $\begingroup$ ... or MyTable[[All, 3]] = 2*Pi*MyTable[[All, 3]] + 42? $\endgroup$
    – kglr
    Commented Sep 27, 2020 at 20:19
  • $\begingroup$ The first one is a good one but that means only one operation on one column at a time right? Won't the second just leave a result with one column? $\endgroup$
    – user27119
    Commented Sep 27, 2020 at 20:23
  • 2
    $\begingroup$ Q.P., if you inspect myTable = Array[a, {4, 4}]; myTable = MapAt[foo, myTable, {All, 3}];myTable and myTable = Array[a, {4, 4}]; myTable[[All, 3]] = foo /@ myTable[[All, 3]];myTable you can see that both methods modify the third column of myTable. $\endgroup$
    – kglr
    Commented Sep 27, 2020 at 20:40
  • $\begingroup$ @kglr, quite right! What about different operations on different columns in the same operation? This is what I really like about OperatedOnTable = MyTable /. {Col1_, Col2_, Col3_, Col4_}->{Col1 + 1/137, Col2, 2*Pi*Col3 + 42, Col4}. It's also very clear to see what one has done, but for more than say eight or ten columns it gets a bit silly. $\endgroup$
    – user27119
    Commented Sep 27, 2020 at 20:49

3 Answers 3

8
$\begingroup$
myTable = Array[Subscript[a, Row[{##}]] &, {5, 10}];

MatrixForm @ myTable

enter image description here

functions = {foo, bar};
columns = {2, 5};

1. Part assignment:

myTable[[All, columns]] = {foo@#, bar@#2} & @@@ myTable[[All, columns]];
myTable // MatrixForm

enter image description here

2. MapAt

myTable = Array[Subscript[a, Row[{##}]] &, {5, 10}];
myTable = Fold[MapAt[First@#2, #, {All, Last@#2}] &, myTable, 
   Transpose[{functions, columns}]];

myTable // MatrixForm

enter image description here

3. ReplacePart

myTable = Array[Subscript[a, Row[{##}]] &, {5, 10}];
myTable = ReplacePart[myTable, 
   MapThread[{i_, #} :> #2[myTable[[i, #]]] &, {columns, functions}]];

myTable // MatrixForm

enter image description here

4. MapIndexed + Association

myTable = Array[Subscript[a, Row[{##}]] &, {5, 10}];
asso = AssociationThread[columns, functions];

myTable = MapIndexed[asso[#2[[2]]]@# /. Missing[__] -> Identity &, myTable, {2}];
myTable // MatrixForm

enter image description here

5. Dataset

myTable = Array[Subscript[a, Row[{##}]] &, {5, 10}];

myTable = Normal @ Dataset[myTable][All, Thread[columns -> functions]];
myTable // MatrixForm

enter image description here

$\endgroup$
1
  • $\begingroup$ Looks good. Let me have a play around and I'll accept. $\endgroup$
    – user27119
    Commented Sep 27, 2020 at 21:12
8
$\begingroup$

A little too long for a comment, and I have marked this as 'Community Wiki' (not an original contribution). In addtion, I think the Query method should not be 'buried' in a comment.

Query

Sjoerd Smit has posted a neat method for applying a function to a matrix column using Query.

   Query[All, {1 -> (#+1/137&),3->(2 Pi# + 42&)}]@myTable2//TeXForm

$$ \left( \begin{array}{cccc} a_{11}+\frac{1}{137} & a_{12} & 2 \pi a_{13}+42 & a_{14} \\ a_{21}+\frac{1}{137} & a_{22} & 2 \pi a_{23}+42 & a_{24} \\ a_{31}+\frac{1}{137} & a_{32} & 2 \pi a_{33}+42 & a_{34} \\ a_{41}+\frac{1}{137} & a_{42} & 2 \pi a_{43}+42 & a_{44} \\ a_{51}+\frac{1}{137} & a_{52} & 2 \pi a_{53}+42 & a_{54} \\ a_{61}+\frac{1}{137} & a_{62} & 2 \pi a_{63}+42 & a_{64} \\ a_{71}+\frac{1}{137} & a_{72} & 2 \pi a_{73}+42 & a_{74} \\ a_{81}+\frac{1}{137} & a_{82} & 2 \pi a_{83}+42 & a_{84} \\ a_{91}+\frac{1}{137} & a_{92} & 2 \pi a_{93}+42 & a_{94} \\ a_{101}+\frac{1}{137} & a_{102} & 2 \pi a_{103}+42 & a_{104} \\ \end{array} \right) $$

 Query[All, {1 -> foo, 3 -> bar, 6-> Sqrt}]@myTable//TeXForm

$$ \left( \begin{array}{cccccc} \text{foo}\left[a_{11}\right] & a_{12} & \text{bar}\left[a_{13}\right] & a_{14} & a_{15} & \sqrt{a_{16}} \\ \text{foo}\left[a_{21}\right] & a_{22} & \text{bar}\left[a_{23}\right] & a_{24} & a_{25} & \sqrt{a_{26}} \\ \text{foo}\left[a_{31}\right] & a_{32} & \text{bar}\left[a_{33}\right] & a_{34} & a_{35} & \sqrt{a_{36}} \\ \text{foo}\left[a_{41}\right] & a_{42} & \text{bar}\left[a_{43}\right] & a_{44} & a_{45} & \sqrt{a_{46}} \\ \text{foo}\left[a_{51}\right] & a_{52} & \text{bar}\left[a_{53}\right] & a_{54} & a_{55} & \sqrt{a_{56}} \\ \end{array} \right) $$

Query (in this case) seems to be based on MapAt 'under the hood'

In[7]:=Query[All, {1 -> (#+ 137&),3->(2 Pi# + 42&)}]@myTable2x

Out[7]= MapAt[#1 + 137 & , {All, 1}][MapAt[2 Pi #1 + 42 & , {All, 3}][myTable2x]]

And for the lazy, the following doesn't seem too bad either:

myTable//MapAt[2 Pi # + 42 & , {All, 3}]// MapAt[# + 137 & , {All, 1}]

$$ \left( \begin{array}{cccccc} a_{11}+137 & a_{12} & 2 \pi a_{13}+42 & a_{14} & a_{15} & a_{16} \\ a_{21}+137 & a_{22} & 2 \pi a_{23}+42 & a_{24} & a_{25} & a_{26} \\ a_{31}+137 & a_{32} & 2 \pi a_{33}+42 & a_{34} & a_{35} & a_{36} \\ a_{41}+137 & a_{42} & 2 \pi a_{43}+42 & a_{44} & a_{45} & a_{46} \\ a_{51}+137 & a_{52} & 2 \pi a_{53}+42 & a_{54} & a_{55} & a_{56} \\ \end{array} \right) $$

Inner

Inner is another possibility (see here)

myTable//Inner[Times,#,ConstantArray[1,Length@#[[1]]],{#1+1/137,#2,#3, 2 Pi #4+42, ##5}&]&//TeXForm

$$ \left( \begin{array}{cccccc} a_{11}+\frac{1}{137} & a_{12} & a_{13} & 2 \pi a_{14}+42 & a_{15} & a_{16} \\ a_{21}+\frac{1}{137} & a_{22} & a_{23} & 2 \pi a_{24}+42 & a_{25} & a_{26} \\ a_{31}+\frac{1}{137} & a_{32} & a_{33} & 2 \pi a_{34}+42 & a_{35} & a_{36} \\ a_{41}+\frac{1}{137} & a_{42} & a_{43} & 2 \pi a_{44}+42 & a_{45} & a_{46} \\ a_{51}+\frac{1}{137} & a_{52} & a_{53} & 2 \pi a_{54}+42 & a_{55} & a_{56} \\ \end{array} \right) $$

Related

Applying a function to every item in a column of a matrix

Matrices

myTable = Array[Subscript[a, Row[{##}]] &, {5, 6}];
myTable2 = Array[Subscript[a, Row[{##}]] &, {10, 4}];
$\endgroup$
1
  • 1
    $\begingroup$ I see you are someone of culture when it comes to choices or arbitrary numbers for examples. $\endgroup$ Commented Sep 29, 2020 at 12:23
7
$\begingroup$

kglr's ReplacePart method is completely general, but I think in many situations writing the rules explicitly is easier and gives more readable code. So I would write:

data = Array[f, {4, 4}];
ReplacePart[data, {{r_, 1} :> data[[r, 1]] + 137, {r_, 3} :> 2 π data[[r, 3]]}]
{{137 + f[1, 1], f[1, 2], 2 π f[1, 3], f[1, 4]}, 
 {137 + f[2, 1], f[2, 2], 2 π f[2, 3], f[2, 4]}, 
 {137 + f[3, 1], f[3, 2], 2 π f[3, 3], f[3, 4]}, 
 {137 + f[4, 1], f[4, 2], 2 π f[4, 3], f[4, 4]}}

I can see what my code does at a glance, but I can't do the with kglr's code. Of course, you may consider this more a reflection on my smarts than a valid critique of kglr's coding style.

Update

The following is added to address concerns raised by the Craig Carter in a comment to this answer.

It's pretty simple. Just change the column specs to the appropriate row specs. Like so:

ReplacePart[data, {{1, c_} :> data[[1, c]] + 137, {3, c_} :> 2 π data[[3, c]]}
{{137 + f[1, 1], 137 + f[1, 2], 137 + f[1, 3], 137 + f[1, 4]}, 
 {f[2, 1], f[2, 2], f[2, 3], f[2, 4]}, 
 {2 π f[3, 1], 2 π f[3, 2], 2 π f[3, 3], 2 π f[3, 4]}, 
 {f[4, 1], f[4, 2], f[4, 3], f[4, 4]}}
$\endgroup$
8
  • $\begingroup$ I have to say this is more what I had in mind. One can immediately ready what this does, superficially it's similar to the example I provided in my question. Would you say there are any limitations with this approach? I ask as you said kglr's solution is more general. $\endgroup$
    – user27119
    Commented Sep 27, 2020 at 22:12
  • 1
    $\begingroup$ @Q.P. This method is also general, but if your table had many, many columns and you were going to modify a lot of them, kglr's code would save some typing, $\endgroup$
    – m_goldberg
    Commented Sep 27, 2020 at 22:22
  • $\begingroup$ The Query method is very nice. Is there a simple way to achieve the same thing for rows? ( e.g. Transpose[ Query[All, {1 -> foo, 2 -> bar, 4 -> Sqrt}]@Transpose[myTable]] ) $\endgroup$ Commented Sep 29, 2020 at 18:55
  • $\begingroup$ @CraigCarter. I don't understand how your comment relates to my answer. It seems to be new question rather than a comment. $\endgroup$
    – m_goldberg
    Commented Sep 29, 2020 at 20:35
  • $\begingroup$ @m_goldberg it is probably a new question because it pertains to rows. Nevertheless, if your answer could be generalized here, it would be instructive for others, I think. $\endgroup$ Commented Sep 29, 2020 at 21:19

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.