Панды оптимизируют сравнение даты и времени по двум столбцам

Как я могу оптимизировать следующую операцию:

df[(df.start <= x) & (df.end >= y)]

Я попытался использовать MultiIndex, но не увидел значительного ускорения.

df = df.set_index(['start', 'end'])
df[(df.index.get_level_values('start') <= end) & (discon_df.index.get_level_values('end') >= start)]

Пример данных:

'<table border = "1" class = "dataframe">\n  <thead>\n    <tr style = "text-align: right;">\n      <th></th>\n      <th>start</th>\n      <th>end</th>\n    </tr>\n  </thead>\n  <tbody>\n    <tr>\n      <th>0</th>\n      <td>2018-11-13 10:28:30.304287</td>\n      <td>2018-11-13 10:46:28.663868</td>\n    </tr>\n    <tr>\n      <th>1</th>\n      <td>2018-11-13 12:27:32.226550</td>\n      <td>2018-11-13 13:09:02.723869</td>\n    </tr>\n    <tr>\n      <th>2</th>\n      <td>2018-11-13 13:29:29.981659</td>\n      <td>2018-11-13 13:54:01.138963</td>\n    </tr>\n    <tr>\n      <th>3</th>\n      <td>2018-11-13 14:30:49.380554</td>\n      <td>2018-11-13 14:48:50.627830</td>\n    </tr>\n    <tr>\n      <th>4</th>\n      <td>2018-11-13 14:59:26.799017</td>\n      <td>2018-11-13 15:24:00.453983</td>\n    </tr>\n    <tr>\n      <th>5</th>\n      <td>2018-11-13 16:30:16.824188</td>\n      <td>2018-11-13 16:48:35.346318</td>\n    </tr>\n    <tr>\n      <th>6</th>\n      <td>2018-11-13 17:15:25.486287</td>\n      <td>2018-11-13 17:59:30.774629</td>\n    </tr>\n    <tr>\n      <th>7</th>\n      <td>2018-11-13 18:27:41.915379</td>\n      <td>2018-11-13 18:47:26.528320</td>\n    </tr>\n    <tr>\n      <th>8</th>\n      <td>2018-11-13 19:28:12.835576</td>\n      <td>2018-11-13 19:52:15.448146</td>\n    </tr>\n    <tr>\n      <th>9</th>\n      <td>2018-11-13 20:41:41.210849</td>\n      <td>2018-11-13 21:07:52.249831</td>\n    </tr>\n    <tr>\n      <th>10</th>\n      <td>2018-11-13 21:11:23.529623</td>\n      <td>2018-11-13 21:42:10.106951</td>\n    </tr>\n  </tbody>\n</table>'

Попробуйте df.query()

Srce Cde 23.11.2018 12:50

@Chirag - он медленнее :(

jezrael 23.11.2018 12:50

Я бы легко решил проблему, если бы был только один столбец, сделав этот столбец DateTimeIndex.

Lokesh 23.11.2018 12:51

Можете ли вы добавить образцы данных?

jezrael 23.11.2018 12:52

Если я столкнусь с этой ситуацией, я разделю это условие. Сначала примените (df.start <= x), затем сохраните его в том же df, а затем примените следующее условие: (df.end >= y) (теперь размер df уменьшен). Может я ошибаюсь, подождем ответов эксперта

Mohamed Thasin ah 23.11.2018 12:54

@jezrael Я добавил несколько примеров данных.

Lokesh 23.11.2018 13:02
Почему в Python есть оператор "pass"?
Почему в Python есть оператор "pass"?
Оператор pass в Python - это простая концепция, которую могут быстро освоить даже новички без опыта программирования.
Некоторые методы, о которых вы не знали, что они существуют в Python
Некоторые методы, о которых вы не знали, что они существуют в Python
Python - самый известный и самый простой в изучении язык в наши дни. Имея широкий спектр применения в области машинного обучения, Data Science,...
Основы Python Часть I
Основы Python Часть I
Вы когда-нибудь задумывались, почему в программах на Python вы видите приведенный ниже код?
LeetCode - 1579. Удаление максимального числа ребер для сохранения полной проходимости графа
LeetCode - 1579. Удаление максимального числа ребер для сохранения полной проходимости графа
Алиса и Боб имеют неориентированный граф из n узлов и трех типов ребер:
Оптимизация кода с помощью тернарного оператора Python
Оптимизация кода с помощью тернарного оператора Python
И последнее, что мы хотели бы показать вам, прежде чем двигаться дальше, это
Советы по эффективной веб-разработке с помощью Python
Советы по эффективной веб-разработке с помощью Python
Как веб-разработчик, Python может стать мощным инструментом для создания эффективных и масштабируемых веб-приложений.
1
6
150
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Узким местом является построение булевой серии / массива, используемого для индексации.

Переход на NumPy дает разумное (~ 2x) улучшение производительности. См. Также: pd.Timestamp против np.datetime64: взаимозаменяемы ли они для отдельных целей?

# boundaries for testing
mindt = pd.to_datetime('2016-01-01') 
maxdt = pd.to_datetime('2017-01-01')

x = ((df['start'] <= mindt) & (df['end'] >= maxdt)).values
y = (df['start'].values <= mindt.to_datetime64()) & (df['end'].values >= maxdt.to_datetime64())

# check results are the same
assert np.array_equal(x, y)

%timeit (df['start'].values <= mindt.to_datetime64()) & (df['end'].values >= maxdt.to_datetime64())
# 55.6 ms per loop

%timeit (df['start'] <= mindt) & (df['end'] >= maxdt)
# 108 ms per loop

Настраивать

np.random.seed(0)

def random_dates(start, end, n):
    start_u = start.value//10**9
    end_u = end.value//10**9
    cols = ['start', 'end']
    df = pd.DataFrame({col: pd.to_datetime(np.random.randint(start_u, end_u, n), unit='s') for col in cols})
    df = pd.DataFrame(np.sort(df.values, axis=1), columns=cols)
    df[cols] = df[cols].apply(pd.to_datetime, errors='raise')
    return df

# construct a dataframe of random dates
df = random_dates(pd.to_datetime('2015-01-01'), pd.to_datetime('2018-01-01'), 10**7)

Большое улучшение. Но я думаю, что основная проблема вызвана тем, что и start, и index неиндексированы и требуют времени O(n). Если бы мы могли проиндексировать их обоих, это было бы намного быстрее. Что вы думаете?

Lokesh 23.11.2018 13:29

@Lokesh, O (п) неизбежно, вам нужно перебирать каждое значение в обеих сериях. Просто сделать это в NumPy более эффективно. Вы также можете стать быстрее с numba, если этого недостаточно.

jpp 23.11.2018 13:30

Вы уверены, что O(n) неизбежен, потому что я заметил значительное улучшение после того, как переместил столбец на DateTimeIndex, а затем запросил его с помощью df[date_time_object:]? Раньше я делал df[df.time > date_time_object], и это было мучительно медленно.

Lokesh 23.11.2018 13:32

@Lokesh, да я уверен. Подумайте об этом теоретически. Чтобы сравнить каждое значение в массиве размера п со скаляром, вы должен повторяете каждое значение в п один раз. Не усложняйте работу. Они не такие. [O (1) может быть медленнее, чем O (п) в определенных случаях, что является еще одной проблемой с этой логикой.]

jpp 23.11.2018 13:35

Но если мы будем использовать двоичный поиск, сравнение сократится до O(log n), и я подозреваю, что DateTimeIndex делает именно это, но я не уверен.

Lokesh 23.11.2018 13:36

Итак, то, что вы предлагаете, работает только если ваши входы отсортированы, а это не ваше предположение. Если ваши входные данные не отсортированы, вам нужно перебрать все элементы. Это неизбежно. Если они отсортированы, я предлагаю вам задать новый вопрос с указанием вашего особого случая и того, что вы пробовали с точки зрения оптимизации.

jpp 23.11.2018 13:37

Нет, мои данные не отсортированы. Я имел в виду, что мы можем сами это отсортировать.

Lokesh 23.11.2018 13:44

@Lokesh, Сортировка будет иметь сложность больше O (п), обычно O (п log п). Но это другой вопрос.

jpp 23.11.2018 13:45

Да, но мне нужно сделать этот запрос примерно 100 000 раз, так что в конечном итоге это неплохо.

Lokesh 23.11.2018 13:46

@Lokesh, конечно: к сожалению, вы не упомянули ни один из этих аспектов в своем исходном вопросе. Итак, задайте новый вопрос с указанием ваших конкретных требований.

jpp 23.11.2018 13:47

Я провел эксперимент, чтобы узнать, использует ли DateTimeIndex двоичный поиск или нет. Оказывается, нет и берет тот же O(n). Если это так, то нет смысла задавать новый вопрос. Было бы слишком сложно построить структуру данных, которая бы выполняла двоичный поиск по дате. Я принимаю этот ответ. Большое спасибо! Ты жжешь.

Lokesh 23.11.2018 13:53

Другие вопросы по теме