Использование All in MapAt in Mathematica


У меня часто есть список пар, как

data = {{0,0.0},{1,12.4},{2,14.6},{3,25.1}}
И я хочу что-то сделать, например Rescale, со всеми вторыми элементами, не касаясь первых элементов. Самый аккуратный способ, который я знаю, это:
Transpose[MapAt[Rescale, Transpose[data], 2]]

Должен быть способ сделать это без такого большого Transpose Инга. Мое желание, чтобы что-то вроде этого сработало:

MapAt[Rescale, data, {All, 2}]
Но я понимаю, что MapAt принимает спецификации Position-стиля вместо спецификаций Part-стиля. Каково правильное решение?

К уточните,

Я ищу решение, в котором мне не нужно повторяться, поэтому мне не хватает double Transpose или double [[All,2]], потому что я считаю повторение сигналом, что я не делаю что-то самым простым способом. Однако, если устранение повторения требует введения промежуточных переменных или именованной функции или другой дополнительной сложности, возможно, решение транспонировать/нетранспортировать уже правильно.
5 10

5 ответов:

Использование Part:

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

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

data

Сначала создайте копию, если это необходимо. (data2 = data затем data2[[All, 2]] и т. д.)


Изменяя мой ответ, чтобы идти в ногу с ответом рубенко, это также можно превратить в функцию:

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

partReplace[data, Rescale, All, 2]

Это довольно общий дизайн.

Я опаздываю на вечеринку, и то, что я опишу, будет очень мало отличаться от того, что есть у Мистера Уизарда, поэтому лучше всего рассматривать этот ответ как дополнение к его решению. Мои частичные оправдания заключаются в том, что, во-первых, функция ниже упаковывает вещи немного иначе и ближе к синтаксису самой MapAt, во-вторых, она немного более общая и имеет возможность использовать с функцией Listable, и в-третьих, я воспроизводю свое решение из прошлого потока Mathgroup именно для этого вопрос, которому уже больше 2-х лет, так что я не плагиат:)

Итак, вот функция:

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]; 

Это та же идея, что @мистер Волшебник использовал, с этими различиями: 1. В случае, когда спецификация не имеет предписанной формы, автоматически будет использоваться обычная MapAt 2. Не все функции являются Listable. Решение мастера @Мистер предполагается, что функция Listable и мы хотим применить его ко всему списку. В приведенном выше коде вы можете указать это с помощью MappedListable вариант.

Я также позаимствую несколько примеров из моего ответа в вышеупомянутой теме:

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}}

Тестирование на больших списках показывает, что использование Listability улучшает производительность, хотя и не так резко здесь:

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}

Это вероятно потому, что для приведенной выше функции (#/10&), Map (который используется внутренне в mapAt для установки MappedListable->False (по умолчанию), смог автоматически компилироваться. В приведенном ниже примере разница более существенна:

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}

В дело в том, что, пока f не былобъявлен Listable, мы знаем, что его тело построено из функций Listable, и поэтому оно может быть применено ко всему списку - но оно не может быть автоматически скомпилировано Map. Обратите внимание, что добавление атрибута Listable к f было бы здесь совершенно неверным и разрушило бы цель, приводя к тому, что mapAt будет медленным в обоих случаях.

Как насчет

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

Который возвращает то, что вы хотите (т. е. он не изменяет data)

Если не разрешено Transpose,

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

Работает.

EDIT: поскольку" самый короткий " теперь цель, лучший из меня до сих пор:

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

На 80 символов, что идентично мистеру Уизарду... Так что голосуйте за его ответ.

Вот еще один подход:

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

op[data, Rescale]

Править 1:

Расширение от мистера мастера, которое не копирует его данные.

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

Используется вот так

partReplace[data, Rescale, All, 2]

Edit 2: Или вот так

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

Это сработало для меня и моего друга

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}}