Использование 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-стиля. Каково правильное решение?
К уточните,
Я ищу решение, в котором мне не нужно повторяться, поэтому мне не хватает doubleTranspose или double [[All,2]], потому что я считаю повторение сигналом, что я не делаю что-то самым простым способом. Однако, если устранение повторения требует введения промежуточных переменных или именованной функции или другой дополнительной сложности, возможно, решение транспонировать/нетранспортировать уже правильно.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. В случае, когда спецификация не имеет предписанной формы, автоматически будет использоваться обычная
MapAt2. Не все функции являются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]]]]