Условное манипулирование данными в Mathematica - PullRequest
8 голосов
/ 26 мая 2011

Я пытаюсь подготовить лучшие инструменты для эффективного анализа данных в Mathematica. У меня есть приблизительно 300 столбцов и 100 000 строк.

Какие бы лучшие уловки для:

«Удалить», «Извлечь» или просто «Рассмотреть» части структуры данных для построения графика, например,

Один из самых хитрых примеров, о которых я мог подумать:

Учитывая структуру данных,

Извлечение столбцов с 1 по 3, с 6 по 9, а также последний столбец для каждой строки, где значение в столбце 2 равно x, а значение в столбце 8 отличается от y

Я также приветствую любые общие советы по манипулированию данными.

Ответы [ 4 ]

6 голосов
/ 26 мая 2011

Для общего манипулирования данными в таблице с именованными столбцами я отсылаю вас к этому моему решению для аналогичного вопроса.В любом конкретном случае было бы проще написать функцию для Select вручную.Тем не менее, для многих столбцов и множества различных запросов шансы испортить индексы высоки.Вот модифицированное решение из упомянутого поста, которое обеспечивает более дружественный синтаксис:

Clear[getIds];
getIds[table : {colNames_List, rows__List}] := {rows}[[All, 1]];

ClearAll[select, where];
SetAttributes[where, HoldAll];
select[cnames_List, from[table : {colNames_List, rows__List}], where[condition_]] :=
With[{colRules =  Dispatch[ Thread[colNames -> Thread[Slot[Range[Length[colNames]]]]]],
    indexRules  =  Dispatch[Thread[colNames -> Range[Length[colNames]]]]},
     With[{selF = Apply[Function, Hold[condition] /. colRules]},
       Select[{rows}, selF @@ # &][[All, cnames /. indexRules]]]];

Здесь происходит то, что функция, используемая в Select, генерируется автоматически из ваших спецификаций.Например (используя пример @ Yoda):

rows = Array[#1 #2 &, {5, 15}];

Нам нужно определить имена столбцов (это должны быть строки или символы без значений):

In[425]:= 
colnames = "c" <> ToString[#] & /@ Range[15]

Out[425]= {"c1", "c2", "c3", "c4", "c5", "c6", "c7", "c8", "c9", "c10", "c11", "c12", 
"c13", "c14", "c15"}

(на практике обычно это именаболее наглядны, конечно).Вот таблица:

table = Prepend[rows, colnames];

Вот оператор SELECT, который вам нужен (я выбрал x = 4 и y=2):

select[{"c1", "c2", "c3", "c6", "c7", "c8", "c9", "c15"}, from[table],
    where["c2" == 4 && "c8" != 2]]

{{2, 4, 6, 12, 14, 16, 18, 30}}

Теперь для одного запроса:это может выглядеть как сложный способ сделать это.Но вы можете выполнять много разных запросов, таких как

In[468]:= select[{"c1", "c2", "c3"}, from[table], where[EvenQ["c2"] && "c10" > 10]]

Out[468]= {{2, 4, 6}, {3, 6, 9}, {4, 8, 12}, {5, 10, 15}}

и т. П.

Конечно, если в ваших данных есть определенные корреляции, вы можете найти специальный алгоритм специального назначения, которыйбудет быстрееПриведенная выше функция может быть расширена различными способами, чтобы упростить общие запросы (включая «все» и т. Д.) Или автоматически скомпилировать сгенерированную чистую функцию (если это возможно).

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

В философской заметке я уверен, что многие пользователи Mathematica (включая меня) время от времени оказывались, снова и снова создавая подобный код.Тот факт, что Mathematica имеет краткий синтаксис, часто очень легко писать для любого конкретного случая.Однако до тех пор, пока кто-то работает в какой-то конкретной области (как, например, манипуляции с данными в таблице), стоимость повторения будет высокой для многих операций.То, что мой пример иллюстрирует в очень простой настройке, является одним из возможных выходов - создать домен-специфический язык (DSL).Для этого обычно нужно определить синтаксис / грамматику для него и написать компилятор из него в Mathematica (чтобы автоматически генерировать код Mathematica).Теперь приведенный выше пример является очень примитивной реализацией этой идеи, но я хочу сказать, что Mathematica, как правило, очень хорошо подходит для создания DSL, что, я думаю, является очень мощной техникой.

4 голосов
/ 26 мая 2011
data = RandomInteger[{1, 20}, {40, 20}]

x = 5;
y = 8;
Select[data, (#[[2]] == x && #[[8]] != y &)][[All, {1, 2, 3, 6, 7, 8, 9, -1}]]

==> {{5, 5, 1, 4, 18, 6, 3, 5}, {10, 5, 15, 3, 15, 14, 2, 5}, {18, 5, 6, 7, 7, 19, 14, 6}}

Некоторые полезные команды для получения частей матрицы и списка: Span (;;), Drop, Take, Select, Cases и другие.См. tutorial / GettingAndSettingPiecesOfMatrices и guide / PartsOfMatrices ,

Part ([[...]]) в сочетании с ;; может быть довольно мощным.a [[All, 1 ;; - 1 ;; 2]], например, означает взять все строки и все нечетные столбцы (-1 имеет обычное значение отсчета с конца).

Selectможет использоваться для выбора элементов из списка (и помните, что матрица представляет собой список списков), основываясь на логической функции.Это брат-близнец Cases, который делает выбор на основе шаблона.Функция, которую я здесь использовал, является «чистой» функцией , где # относится к аргументу, к которому применяется эта функция (элементы списка в данном случае).Поскольку сами элементы являются списками (строками матрицы), я могу ссылаться на столбцы с помощью функции Part ([[..]]).

1 голос
/ 26 мая 2011

Я прочитал:

Извлечение столбцов с 1 по 3, с 6 по 9, а также последний столбец для каждой строки, где значение в столбце 2 равно x, а значение в столбце 8 -отличается от y

как означающее, что мы хотим:

  • элементы 1-3 и 6-9 из каждой строки

AND

  • последний элемент из строк, в котором [[2]] == x && [[8]] != y.

Это то, что я взломал вместе:

a = RandomInteger[5, {20, 10}];          (*define the array*)
x = 4; y = 0;                            (*define the test values*)

Join @@ Range @@@ {1 ;; 3, 6 ;; 9};      (*define the column ranges*)

#2 == x && #8 != y & @@@ a;              (*test the rows*)

Append[%%, #] & /@ % /. {True -> -1, False :> Sequence[]};  (*complete the ranges according to the test*)

MapThread[Part, {a, %}] // TableForm     (*extract and display*)
1 голос
/ 26 мая 2011

Чтобы вытащить столбцы (или строки), вы можете сделать это путем индексации детали

data = Array[#1 #2 &, {5, 15}];
data[[All, Flatten@{Range@3, Range @@ {6, 9}, -1}]]

MatrixForm@%

Последняя строка предназначена только для просмотра.

Как отметил Шёрд в своем комментарии (и в объяснении в своем ответе), индексирование одного диапазона можно легко выполнить с помощью команды Span (;;) .Если вы объединяете несколько непересекающихся диапазонов, использовать Flatten для объединения отдельных диапазонов, созданных с помощью Range, проще, чем вводить их вручную.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...