#python #pandas
#python #pandas
Вопрос:
Мне интересно, есть ли эффективный способ сделать это в pandas: учитывая фрейм данных, какова первая строка, которая меньше заданного значения? Например, учитывая:
addr
0 4196656
1 4197034
2 4197075
3 4197082
4 4197134
Какое первое значение меньше 4197080? Я хочу, чтобы он возвращал только строку с 4197075.
Решением было бы сначала отфильтровать по 4197080, а затем взять последнюю строку, но это выглядит как чрезвычайно медленная операция O (N) (сначала построение фрейма данных, а затем его последняя строка), в то время как двоичный поиск займет O (logN).
df.addr[ df.addr < 4197080].tail(1)
Я рассчитал время, и создание df.addr[ df.addr < 4197080]
более или менее занимает то же df.addr[ df.addr < 4197080].tail(1)
самое, что и, сильно намекая, что внутренне он сначала создает весь df.
num = np.random.randint(0, 10**8, 10**6)
num.sort()
df = pd.DataFrame({'addr':num})
df = df.set_index('addr', drop=False)
df = df.sort_index()
Получение первого меньшего значения происходит очень медленно:
%timeit df.addr[ df.addr < 57830391].tail(1)
100 loops, best of 3: 7.9 ms per loop
Использование lt немного улучшает ситуацию:
%timeit df.lt(57830391)[-1:]
1000 loops, best of 3: 853 µs per loop
Но все равно далеко не так быстро, как двоичный поиск:
%timeit bisect(num, 57830391, 0, len(num))
100000 loops, best of 3: 6.53 µs per loop
Есть ли лучший способ?
Комментарии:
1. использование
bisect
кажется довольно быстрым. так что продолжайте в том же духе. почему эта разница во времени действительно имеет значение для практичности? вы делаете это несколько раз? (в котором их лучшие способы). какую реальную проблему вы решаете?2. К вашему СВЕДЕНИЮ, сортировка здесь, безусловно, самая медленная операция. вы можете попробовать использовать
nsmallest
, который НЕ сортируется. (новое в 0.14.0)3. Но я только определяю время запросов, а не сортирую.. Да, я буду выполнять этот запрос много раз, может быть, тысячи или миллионы раз. Фактическую проблему здесь немного сложно объяснить.. это только часть общего алгоритма.
4. Тогда смотрите мой ответ. Если вы знаете, что он отсортирован, то это довольно хорошее время запроса.
5. и использовать
searchsorted
НАМНОГО быстрее, посколькуbisect
эффективно работает с массивами numpy assearchsorted
.
Ответ №1:
Для этого требуется 0.14.0
Обратите внимание, что фрейм НЕ ОТСОРТИРОВАН.
In [16]: s = df['addr']
Найти наибольшее значение ниже требуемого
In [18]: %timeit s[s<5783091]
100 loops, best of 3: 9.01 ms per loop
In [19]: %timeit s[s<5783091].nlargest(1)
100 loops, best of 3: 11 ms per loop
Таким образом, это быстрее, чем фактическое выполнение полной сортировки с последующей индексацией.
Цель .copy
состоит в том, чтобы избежать смещения сортировки на месте.
In [32]: x = np.random.randint(0, 10**8, 10**6)
In [33]: def f(x):
....: x.copy().sort()
....:
In [35]: %timeit f(x)
10 loops, best of 3: 67.2 ms per loop
Если вы просто ищете УЖЕ ОТСОРТИРОВАННУЮ серию, тогда используйте searchsorted
. Обратите внимание, что вы должны использовать версию numpy (например, operate on .values
. Версия серии будет определена в 0.14.1)
In [41]: %timeit s.values.searchsorted(5783091)
100000 loops, best of 3: 2.5 µs per loop
Комментарии:
1. Да, разделение пополам кажется очень быстрым, намного быстрее, чем все, что я могу сделать в pandas. Вопрос заключался в том, смогу ли я создавать pandas так же быстро.