Использование 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. В случае, когда спецификация не имеет предписанной формы, автоматически будет использоваться обычная
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]]]]