Using All in MapAt in Mathematica

左心房为你撑大大i 提交于 2020-01-02 01:16:14

问题


I often have a list of pairs, as

data = {{0,0.0},{1,12.4},{2,14.6},{3,25.1}}

and I want to do something, for instance Rescale, to all of the second elements without touching the first elements. The neatest way I know is:

Transpose[MapAt[Rescale, Transpose[data], 2]]

There must be a way to do this without so much Transposeing. My wish is for something like this to work:

MapAt[Rescale, data, {All, 2}]

But my understanding is that MapAt takes Position-style specifications instead of Part-style specifications. What's the proper solution?

To clarify,

I'm seeking a solution where I don't have to repeat myself, so lacking double Transpose or double [[All,2]], because I consider repetition a signal I'm not doing something the easiest way. However, if eliminating the repetition requires the introduction of intermediate variables or a named function or other additional complexity, maybe the transpose/untranspose solution is already correct.


回答1:


Use Part:

data = {{0, 0.0}, {1, 12.4}, {2, 14.6}, {3, 25.1}}

data[[All, 2]] = Rescale @ data[[All, 2]];

data

Create a copy first if you need to. (data2 = data then data2[[All, 2]] etc.)


Amending my answer to keep up with ruebenko's, this can be made into a function also:

partReplace[dat_, func_, spec__] :=
  Module[{a = dat},
    a[[spec]] = func @ a[[spec]];
    a
  ]

partReplace[data, Rescale, All, 2]

This is quite general is design.




回答2:


I am coming late to the party, and what I will describe will differ very little with what @Mr. Wizard has, so it is best to consider this answer as a complementary to his solution. My partial excuses are that first, the function below packages things a bit differently and closer to the syntax of MapAt itself, second, it is a bit more general and has an option to use with Listable function, and third, I am reproducing my solution from the past Mathgroup thread for exactly this question, which is more than 2 years old, so I am not plagiarizing :)

So, here is the function:

ClearAll[mapAt,MappedListable]; 
Protect[MappedListable]; 
Options[mapAt] = {MappedListable -> False}; 
mapAt[f_, expr_, {pseq : (All | _Integer) ..}, OptionsPattern[]] := 
  Module[{copy = expr}, 
    copy[[pseq]] = 
      If[TrueQ[OptionValue[MappedListable]] && Head[expr] === List, 
        f[copy[[pseq]]], 
        f /@ copy[[pseq]] 
      ]; 
    copy]; 
mapAt[f_, expr_, poslist_List] := MapAt[f, expr, poslist]; 

This is the same idea as what @Mr. Wizard used, with these differences: 1. In case when the spec is not of the prescribed form, regular MapAt will be used automatically 2. Not all functions are Listable. The solution of @Mr.Wizard assumes that either a function is Listable or we want to apply it to the entire list. In the above code, you can specify this by the MappedListable option.

I will also borrow a few examples from my answer in the above-mentioned thread:

In[18]:= mat=ConstantArray[1,{5,3}];

In[19]:= mapAt[#/10&,mat,{All,3}]
Out[19]= {{1,1,1/10},{1,1,1/10},{1,1,1/10},{1,1,1/10},{1,1,1/10}}

In[20]:= mapAt[#/10&,mat,{3,All}]
Out[20]= {{1,1,1},{1,1,1},{1/10,1/10,1/10},{1,1,1},{1,1,1}}

Testing on large lists shows that using Listability improves the performance, although not so dramatically here:

In[28]:= largemat=ConstantArray[1,{150000,15}];

In[29]:= mapAt[#/10&,largemat,{All,3}];//Timing
Out[29]= {0.203,Null}

In[30]:= mapAt[#/10&,largemat,{All,3},MappedListable->True];//Timing
Out[30]= {0.094,Null}

This is likely because for the above function (#/10&), Map (which is used internally in mapAt for the MappedListable->False (default) setting, was able to auto-compile. In the example below, the difference is more substantial:

ClearAll[f];
f[x_] := 2 x - 1;

In[54]:= mapAt[f,largemat,{All,3}];//Timing
Out[54]= {0.219,Null}

In[55]:= mapAt[f,largemat,{All,3},MappedListable->True];//Timing
Out[55]= {0.031,Null}

The point is that, while f was not declared Listable, we know that its body is built out of Listable functions, and thus it can be applied to the entire list - but OTOH it can not be auto-compiled by Map. Note that adding Listable attribute to f would have been completely wrong here and would destroy the purpose, leading to mapAt being slow in both cases.




回答3:


How about

Transpose[{#[[All, 1]], Rescale[#[[All, 2]]]} &@data]

which returns what you want (ie, it does not alter data)

If no Transpose is allowed,

Thread[Join[{#[[All, 1]], Rescale[#[[All, 2]]]} &@data]]

works.

EDIT: As "shortest" is now the goal, best from me so far is:

data\[LeftDoubleBracket]All, 2\[RightDoubleBracket] = Rescale[data[[All, 2]]]

at 80 characters, which is identical to Mr.Wizard's... So vote for his answer.




回答4:


Here is another approach:

op[data_List, fun_] := 
 Join[data[[All, {1}]], fun[data[[All, {2}]]], 2]

op[data, Rescale]

Edit 1:

An extension from Mr.Wizard, that does not copy it's data.

SetAttributes[partReplace, HoldFirst]
partReplace[dat_, func_, spec__] := dat[[spec]] = func[dat[[spec]]];

used like this

partReplace[data, Rescale, All, 2]

Edit 2: Or like this

ReplacePart[data, {All, 2} -> Rescale[data[[All, 2]]]]



回答5:


This worked for me and a friend

In[128]:= m = {{x, sss, x}, {y, sss, y}}
Out[128]= {{2, sss, 2}, {y, sss, y}}

In[129]:= function[ins1_] := ToUpperCase[ins1];
fatmap[ins2_] := MapAt[function, ins2, 2];

In[131]:= Map[fatmap, m]
Out[131]= {{2, ToUpperCase[sss], 2}, {y, ToUpperCase[sss], y}}


来源:https://stackoverflow.com/questions/8580113/using-all-in-mapat-in-mathematica

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!