Выбор данных из таблицы в Mathematica
Я пытаюсь написать функцию, которая будет выбирать первый элемент в таблице, который удовлетворяет критериям. Например, если мне дается следующая таблица со временем в первом столбце и числом людей, зараженных болезнью, во втором, я хочу написать аргумент, который будет возвращать время, когда заражено не менее 100 человек.
0 1
1 2
2 4
3 8
4 15
5 29
6 50
7 88
8 130
9 157
10 180
11 191
12 196
13 199
14 200
Итак, из этой таблицы я хочу, чтобы аргумент сказал мне, что за 8 секунд заразилось не менее 100 человек. Я попытался использовать SELECT, чтобы сделать это, но я не уверен, как использовать SELECT с таблицей из 2 столбцов и сделать так, чтобы он возвращал значение в первом столбце на основе критериев из второго столбца.
4 ответа
Вы также можете использовать простое NestWhile
data = {{0,1},{1,2},{2,4},{3,8},{4,15},{5,29},{6,50},{7,88},{8,130},{9,157},{10,180},
{11,191},{12,196},{13,199},{14,200}};
NestWhile[# + 1 &, 1, data[[#, 2]] < 100 &] - 1
Альтернатива, которая использует правила замены
ImportString["0 1 1 2 2 4 3 8 4 15 5 29 6 50 7 88 8 130 9 157 10 180 11 191 12 196 13 199 14 200", "Table"];
Partition[Flatten[%], 2]
% /. {___, x : {_, _?(# >= 100 &)}, ___} :> x
Алгоритм, с помощью которого Mathematica ищет шаблоны, гарантирует, что это вернет первый такой случай. Если вы хотите все случаи, то вы можете использовать ReplaceList. Я предлагаю вам прочитать учебник по шаблонам и правилам.
Редактировать: ImportString
также работает с вновь отформатированными данными - но вам больше не нужно использовать Partition
,
Вот несколько способов сделать это, если я правильно интерпретировал ваши данные...
In[3]:= data = {{0,1},{1,2},{2,4},{3,8},{4,15},{5,29},{6,50},{7,88},{8,130},{9,157},{10,180},{11,191},{12,196},{13,199},{14,200}};
In[8]:= Cases[data, {_, _?(#>=100&)}, 1, 1][[1, 1]]
Out[8]= 8
In[9]:= Select[data, #[[2]]>=100&, 1][[1, 1]]
Out[9]= 8
Я предлагаю вам прочитать часть [], чтобы лучше это понять.
Я верю, что есть более быстрый путь, чем тот, который уже был дан, но, во-первых, Иисус Навин Cases
метод можно сделать немного быстрее, используя /;
скорее, чем &
для теста.
Это решение, которое я предлагаю (отредактируйте: добавив пробел для ясности, поскольку здесь двойные скобки не форматируются):
dat[[
Position[
dat[[All, 2]],
x_ /; x >= 100,
1, 1
][[1, 1]],
1
]]
Вот время для различных предлагаемых методов. Обратите внимание, что /.
метод запускается только один раз, в то время как другие запускаются loops
раз. Поэтому в этом первом тесте он в 100 раз медленнее, чем Position
метод. Так же NestWhile
Метод возвращает только индекс, а не фактический элемент первого столбца.
In[]:=
dat = {Range[5000], Sort@RandomInteger[1*^6, 5000]} // Transpose;
lim = 300000; loops = 100;
dat /. {___, {x_, _?(# >= lim &)}, ___} :> x; // Timing
Do[ Cases[dat, {_, _?(# >= lim &)}, 1, 1][[1, 1]] , {loops}] // Timing
Do[ Cases[dat, {_, y_ /; y >= lim}, 1, 1][[1, 1]] , {loops}] // Timing
Do[ Select[dat, #[[2]] >= lim &, 1][[1, 1]] , {loops}] // Timing
Do[ NestWhile[# + 1 &, 1, dat[[#, 2]] < lim &] , {loops}] // Timing
Do[ dat[[Position[dat[[All, 2]], x_ /; x >= lim, 1, 1][[1, 1]], 1]] , {loops}] // Timing
Out[]= {0.125, Null}
Out[]= {0.438, Null}
Out[]= {0.406, Null}
Out[]= {0.469, Null}
Out[]= {0.281, Null}
Out[]= {0.125, Null}
С более длинным столом (я опускаю медленный метод):
In[]:=
dat = {Range[35000], Sort@RandomInteger[1*^6, 35000]} // Transpose;
lim = 300000; loops = 25;
Do[ Cases[dat, {_, _?(# >= lim &)}, 1, 1][[1, 1]] , {loops}] // Timing
Do[ Cases[dat, {_, y_ /; y >= lim}, 1, 1][[1, 1]] , {loops}] // Timing
Do[ Select[dat, #[[2]] >= lim &, 1][[1, 1]] , {loops}] // Timing
Do[ NestWhile[# + 1 &, 1, dat[[#, 2]] < lim &] , {loops}] // Timing
Do[ dat[[Position[dat[[All, 2]], x_ /; x >= lim, 1, 1][[1, 1]], 1]] , {loops}] // Timing
Out[]= {0.734, Null}
Out[]= {0.641, Null}
Out[]= {0.734, Null}
Out[]= {0.5, Null}
Out[]= {0.266, Null}
Наконец, подтверждение соглашения:
In[]:= SameQ[
Select[dat, #[[2]] >= lim &, 1][[1, 1]],
dat[[Position[dat[[All, 2]], x_ /; x >= lim, 1, 1][[1, 1]], 1]]
]
Out[]= True