Mathematica: условные операции над списками - PullRequest
4 голосов
/ 25 мая 2011

Я бы хотел усреднить по "Rows" в столбце. То есть строки, имеющие одинаковое значение в другом столбце.

Например:

e= {{1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2}, 
   {69, 7, 30, 38, 16, 70, 97, 50, 97, 31, 81, 96, 60, 52, 35, 6, 
    24, 65, 76, 100}}

enter image description here

Я бы хотел усреднить все значения во втором столбце, которые имеют одинаковое значение в первом.

Итак, здесь: Среднее для Col 1 = 1 и Col 1 = 2

А затем создайте третий столбец с результатом этой операции. Таким образом, значения в этих столбцах должны быть одинаковыми для первых 10 строк следующих 10.

Большое спасибо за любую помощь, которую вы можете оказать!

LA

Выходной идеальный формат:

enter image description here

Ответы [ 5 ]

5 голосов
/ 25 мая 2011

Интересная проблема.Это первое, что пришло мне в голову:

e[[All, {1}]] /. Reap[Sow[#2, #] & @@@ e, _, # -> Mean@#2 &][[2]];

ArrayFlatten[{{e, %}}] // TableForm

Чтобы получить округление, вы можете просто добавить Round@ до Mean в коде выше: Round@Mean@#2

Вотнемного более быстрый метод, но я на самом деле предпочитаю Sow / Reap один выше:

#[[1, 1]] -> Round@Mean@#[[All, 2]] & /@ GatherBy[e, First];

ArrayFlatten[{{e, e[[All, {1}]] /. %}}] // TableForm

Если у вас много разных элементов в первом столбце, любое из приведенных выше решений может быть сделано быстрее с помощьюприменение Dispatch к производимому списку правил перед выполнением замены (/.).Эта команда указывает Mathematica создать и использовать оптимизированный внутренний формат для списка правил.

Вот вариант, который работает медленнее, но мне все равно его достаточно поделиться:

Module[{q},
  Reap[{#, Sow[#2,#], q@#} & @@@ e, _, (q@# = Mean@#2) &][[1]]
]

Также общие советы вы можете заменить:

Table[RandomInteger[{1, 100}], {20}] на RandomInteger[{1, 100}, 20]

и Join[{c}, {d}] // Transpose на Transpose[{c, d}].

4 голосов
/ 25 мая 2011

Какого черта, я присоединюсь к вечеринке. Вот моя версия:

Flatten/@Flatten[Thread/@Transpose@{#,Mean/@#[[All,All,2]]}&@GatherBy[e,First],1]

Думаю, должно быть достаточно быстро.

EDIT

В ответ на критику @ Mr.Wizard (моим первым решением было изменение порядка в списке), а также для небольшого изучения высокопроизводительного угла проблемы, есть 2 альтернативных решения:

getMeans[e_] := 
Module[{temp = ConstantArray[0, Max[#[[All, 1, 1]]]]},
  temp[[#[[All, 1, 1]]]] = Mean /@ #[[All, All, 2]];
  List /@ temp[[e[[All, 1]]]]] &[GatherBy[e, First]];

getMeansSparse[e_] := 
Module[{temp = SparseArray[{Max[#[[All, 1, 1]]] -> 0}]},
  temp[[#[[All, 1, 1]]]] = Mean /@ #[[All, All, 2]];
  List /@ Normal@temp[[e[[All, 1]]]]] &[GatherBy[e, First]];

Первый является самым быстрым, торгующим памятью на скорость, и может применяться, когда все ключи являются целыми числами, и ваше максимальное значение "ключа" (2 в вашем примере) не слишком велико. Второе решение свободно от последнего ограничения, но медленнее. Вот большой список пар:

In[303]:= 
tst = RandomSample[#, Length[#]] &@
   Flatten[Map[Thread[{#, RandomInteger[{1, 100}, 300]}] &, 
      RandomSample[Range[1000], 500]], 1];

In[310]:= Length[tst]

Out[310]= 150000

In[311]:= tst[[;; 10]]

Out[311]= {{947, 52}, {597, 81}, {508, 20}, {891, 81}, {414, 47}, 
{849, 45}, {659, 69}, {841, 29}, {700, 98}, {858, 35}}

Здесь ключей может быть от 1 до 1000, из них 500, и для каждого ключа есть 300 случайных чисел. Теперь некоторые показатели:

In[314]:= (res0 = getMeans[tst]); // Timing

Out[314]= {0.109, Null}

In[317]:= (res1 = getMeansSparse[tst]); // Timing

Out[317]= {0.219, Null}

In[318]:= (res2 =  tst[[All, {1}]] /. 
 Reap[Sow[#2, #] & @@@ tst, _, # -> Mean@#2 &][[2]]); // Timing

Out[318]= {5.687, Null}

In[319]:= (res3 = tst[[All, {1}]] /. 
 Dispatch[
  Reap[Sow[#2, #] & @@@ tst, _, # -> Mean@#2 &][[2]]]); // Timing

Out[319]= {0.391, Null}

In[320]:= res0 === res1 === res2 === res3

Out[320]= True

Мы можем видеть, что getMeans является самым быстрым здесь, getMeansSparse вторым самым быстрым, и решение @ Mr.Wizard несколько медленнее, но только когда мы используем Dispatch, иначе это намного медленнее. Мои и @ Mr.Wizard решения (с Dispatch) схожи по духу, разница в скорости происходит из-за (разреженной) индексации массива, которая более эффективна, чем поиск по хешу. Конечно, все это имеет значение только тогда, когда ваш список действительно большой.

РЕДАКТИРОВАТЬ 2

Вот версия getMeans, которая использует Compile с целью C и возвращает числовые значения (а не рациональные). Это примерно в два раза быстрее, чем getMeans, и самое быстрое из моих решений.

getMeansComp = 
 Compile[{{e, _Integer, 2}},
   Module[{keys = e[[All, 1]], values = e[[All, 2]], sums = {0.} ,
      lengths = {0}, , i = 1, means = {0.} , max = 0, key = -1 , 
      len = Length[e]},
    max = Max[keys];
    sums = Table[0., {max}];
    lengths = Table[0, {max}];
    means = sums;
    Do[key = keys[[i]];
      sums[[key]] += values[[i]];
      lengths[[key]]++, {i, len}];
    means = sums/(lengths + (1 - Unitize[lengths]));
    means[[keys]]], CompilationTarget -> "C", RuntimeOptions -> "Speed"]

getMeansC[e_] := List /@ getMeansComp[e];

Код 1 - Unitize[lengths] защищает от деления на ноль для неиспользованных ключей. Нам нужен каждый номер в отдельном подсписке, поэтому мы должны звонить getMeansC, а не getMeansComp напрямую. Вот некоторые измерения:

In[180]:= (res1 = getMeans[tst]); // Timing

Out[180]= {0.11, Null}

In[181]:= (res2 = getMeansC[tst]); // Timing

Out[181]= {0.062, Null}

In[182]:= N@res1 == res2

Out[182]= True

Вероятно, это можно считать сильно оптимизированным численным решением. Тот факт, что полное общее, краткое и красивое решение @ Mr.Wizard медленнее всего в 6-8 раз, говорит о хорошем общем кратком решении, поэтому, если вы не хотите выжать каждую микросекунду из него, я бы придерживаться @ Mr.Wizard's (с Dispatch). Но важно знать, как оптимизировать код, а также в какой степени его можно оптимизировать (чего можно ожидать).

3 голосов
/ 25 мая 2011

Наивный подход может быть:

Table[
  Join[ i, {Select[Mean /@ SplitBy[e, First], First@# == First@i &][[1, 2]]}]
, {i, e}] // TableForm

(*
1   59  297/5
1   72  297/5
1   90  297/5
1   63  297/5
1   77  297/5
1   98  297/5
1   3   297/5
1   99  297/5
1   28  297/5
1   5   297/5
2   87  127/2
2   80  127/2
2   29  127/2
2   70  127/2
2   83  127/2
2   75  127/2
2   68  127/2
2   65  127/2
2   1   127/2
2   77  127/2
*)

Вы также можете создать свой оригинальный список, используя, например:

e = Array[{Ceiling[#/10], RandomInteger[{1, 100}]} &, {20}]

Редактировать

Отвечая на комментарии г-на

Если список не отсортирован по первому элементу, вы можете сделать:

Table[Join[
  i, {Select[
     Mean /@ SplitBy[SortBy[e, First], First], First@# == First@i &][[1,2]]}],
{i, e}] //TableForm

Но в вашем примере это не обязательно

2 голосов
/ 25 мая 2011

Почему бы не навалить?

Я думал, что это был самый простой / легко читаемый ответ, хотя и не обязательно самый быстрый. Но это действительно удивительно, сколько способов вы можете думать о такой проблеме в Mathematica.

г. Волшебник, очевидно, очень крутой, как отмечали другие.

@ Nasser, ваше решение не обобщается на n-классы, хотя его легко можно изменить для этого.

meanbygroup[table_] := Join @@ Table[
   Module[
     {sublistmean},
     sublistmean = Mean[sublist[[All, 2]]];
     Table[Append[item, sublistmean], {item, sublist}]
   ]
   , {sublist, GatherBy[table, #[[1]] &]}
       ]
(* On this dataset: *) 
meanbygroup[e] 
1 голос
/ 25 мая 2011

Ух ты, ответы здесь такие продвинутые и классные, Нужно больше времени, чтобы изучить их.

Вот мой ответ, я до сих пор работаю в области матрицы / вектора / Matlab'ish в области восстановления и перехода, поэтому мое решение не является функциональным, как решение экспертов здесь, я рассматриваю данные как матрицы и векторы (для меня это проще, чем смотреть у них как списки списков и т.д ...) так вот оно


sizeOfList=10; (*given from the problem, along with e vector*)
m1 = Mean[e[[1;;sizeOfList,2]]];
m2 = Mean[e[[sizeOfList+1;;2 sizeOfList,2]]];
r  = {Flatten[{a,b}], d , Flatten[{Table[m1,{sizeOfList}],Table[m2,{sizeOfList}]}]} //Transpose;

MatrixForm[r]

Очевидно, что не такое хорошее решение, как функциональные.

Хорошо, я сейчас пойду и спрячусь от функциональных программистов:)

- Насер

...