Использование All in MapAt в Mathematica
У меня часто есть список пар, поскольку
data = {{0,0.0},{1,12.4},{2,14.6},{3,25.1}}
и я хочу сделать что-то, например Rescale
, ко всем вторым элементам, не касаясь первых элементов. Самый простой способ, который я знаю, это:
Transpose[MapAt[Rescale, Transpose[data], 2]]
Должен быть способ сделать это без большого количества Transpose
ing. Мое желание состоит в том, чтобы что-то вроде этого работать:
MapAt[Rescale, data, {All, 2}]
Но я понимаю, что MapAt
использует Position
-стандартные спецификации вместо спецификаций Part
. Какое правильное решение?
Чтобы уточнить,
Я ищу решение, в котором мне не нужно повторять себя, поэтому мне не хватает двойного Transpose
или double [[All,2]]
, потому что я считаю повторение сигнала, что я не делаю что-то самым простым способом. Однако, если исключение повторения требует введения промежуточных переменных или именованной функции или другой дополнительной сложности, возможно, решение транспонирования/нетранслирования уже правильно.
Ответы
Ответ 1
Используйте 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]]
и т.д.)
Внесение изменений в мой ответ, чтобы не отставать от ruebenko's, это также можно сделать в функцию:
partReplace[dat_, func_, spec__] :=
Module[{a = dat},
a[[spec]] = func @ a[[spec]];
a
]
partReplace[data, Rescale, All, 2]
Это довольно общий дизайн.
Ответ 2
Я опаздываю на вечеринку, и то, что я опишу, будет очень мало отличаться от того, что @Mr. У мастера есть, поэтому лучше всего рассматривать этот ответ как дополнение к его решению. Мои частичные оправдания состоят в том, что вначале функция ниже упаковывает вещи немного по-другому и ближе к синтаксису самого MapAt
, во-вторых, она немного более общая и имеет возможность использовать с функцией Listable
, а в-третьих, я Я воспроизводил свое решение из прошлой темы Mathgroup именно по этому вопросу, которому больше двух лет, поэтому я не плагирую:)
Итак, вот функция:
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];
Это та же идея, что и @Mr. Используется мастер с этими отличиями: 1. Если спецификация не соответствует установленной форме, обычный MapAt
будет использоваться автоматически 2. Не все функции Listable
. Решение @Mr.Wizard предполагает, что либо функция 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
, и поэтому оно может быть применено ко всему списку, но OTOH не может быть автоматически скомпилирован Map
. Обратите внимание, что добавление атрибута Listable
к f
было бы совершенно неправильным здесь и уничтожило бы цель, в результате чего MapAt
будет медленным в обоих случаях.
Ответ 3
Как насчет
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 символов, что совпадает с Mr.Wizard's... Итак, проголосуйте за его ответ.
Ответ 4
Вот еще один подход:
op[data_List, fun_] :=
Join[data[[All, {1}]], fun[data[[All, {2}]]], 2]
op[data, Rescale]
Изменить 1:
Расширение от Mr.Wizard, которое не копирует его.
SetAttributes[partReplace, HoldFirst]
partReplace[dat_, func_, spec__] := dat[[spec]] = func[dat[[spec]]];
используется как
partReplace[data, Rescale, All, 2]
Изменить 2:
Или как это
ReplacePart[data, {All, 2} -> Rescale[data[[All, 2]]]]
Ответ 5
Это сработало для меня и друга
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}}