Pandas: Как ограничить результаты str.contains?

У меня есть DataFrame с> 1 млн строк. Я хотел бы выбрать все строки, в которых определенный столбец содержит определенную подстроку:

matching = df['col2'].str.contains('substr', case=True, regex=False)
rows = df[matching].col1.drop_duplicates()

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

matching = df['col2'].str.contains('substr', case=True, regex=False).head(n)

а также:

matching = df['col2'].str.contains('substr', case=True, regex=False).sample(n)

но они не быстрее. Второй оператор является логическим и очень быстрым. Как я могу ускорить первую выписку?

Почему в 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 может стать мощным инструментом для создания эффективных и масштабируемых веб-приложений.
3
0
567
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

Вы можете ускорить это с помощью:

matching = df['col2'].head(n).str.contains('substr', case=True, regex=False)
rows = df['col1'].head(n)[matching==True]

Однако это решение будет получать результаты сопоставления в первых строках n, а не в первых результатах сопоставления n.

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

rows =  df['col1'][df['col2'].str.contains("substr")==True].head(n)

Но этот вариант, конечно, намного медленнее.

Вдохновленный ответом @ ScottBoston, вы можете использовать следующий подход для полное более быстрое решение:

rows = df['col1'][pd.Series(['substr' in i for i in df['col2']])==True].head(n)

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

С помощью тестовый код ниже мы можем увидеть, насколько быстро каждое решение и его результаты:

import pandas as pd
import time

n = 10
a = ["Result", "from", "first", "column", "for", "this", "matching", "test", "end"]
b = ["This", "is", "a", "test", "has substr", "also has substr", "end", "of", "test"]

col1 = a*1000000
col2 = b*1000000

df = pd.DataFrame({"col1":col1,"col2":col2})

# Original option
start_time = time.time()
matching = df['col2'].str.contains('substr', case=True, regex=False)
rows = df[matching].col1.drop_duplicates()
print("--- %s seconds ---" % (time.time() - start_time))

# Faster option
start_time = time.time()
matching_fast = df['col2'].head(n).str.contains('substr', case=True, regex=False)
rows_fast = df['col1'].head(n)[matching==True]
print("--- %s seconds for fast solution ---" % (time.time() - start_time))


# Other option
start_time = time.time()
rows_other =  df['col1'][df['col2'].str.contains("substr")==True].head(n)
print("--- %s seconds for other solution ---" % (time.time() - start_time))

# Complete option
start_time = time.time()
rows_complete = df['col1'][pd.Series(['substr' in i for i in df['col2']])==True].head(n)
print("--- %s seconds for complete solution ---" % (time.time() - start_time))

Это выведет:

>>> 
--- 2.33899998665 seconds ---
--- 0.302999973297 seconds for fast solution ---
--- 4.56700015068 seconds for other solution ---
--- 1.61599993706 seconds for complete solution ---

И результирующая серия будет:

>>> rows
4     for
5    this
Name: col1, dtype: object
>>> rows_fast
4     for
5    this
Name: col1, dtype: object
>>> rows_other
4      for
5     this
13     for
14    this
22     for
23    this
31     for
32    this
40     for
41    this
Name: col1, dtype: object
>>> rows_complete
4      for
5     this
13     for
14    this
22     for
23    this
31     for
32    this
40     for
41    this
Name: col1, dtype: object

Это не совсем ответ на мой вопрос. Я скептически относился к ограничению пространства поиска: это, очевидно, повысит производительность, но за счет результатов. Однако, попробовав ваше «более быстрое» решение с n = 10000, результаты неплохие, а время значительно улучшилось. Но, в конце концов, я не могу развернуть это «более быстрое» решение, потому что оно предполагает совпадение в пределах первых результатов п, что может быть неверным! Я отредактирую свой вопрос, чтобы прояснить это.

Julio 15.03.2018 20:49

Да, я предполагал, что вам нужны первые совпадения n, а не совпадения в первых рядах n. Я найду способ улучшить тайминги, если это вам поможет. Может быть, ответ @ScottBoston - довольно хорошее решение

Cedric Zoppolo 15.03.2018 20:51

Имейте в виду, что ваше решение также возвращает совпадения в первых строках n.

Cedric Zoppolo 15.03.2018 20:55

Верно. Действительно, ваше «другое» решение возвращает первые n совпадений, но это медленнее, чем при полном отсутствии .head(), т.е. без ограничения поиска.

Julio 15.03.2018 21:04

Пожалуйста, посмотрите мое обновление. Я считаю, что «полное решение» - это довольно хороший подход.

Cedric Zoppolo 15.03.2018 21:16

Пожалуйста, проголосуйте за и / или примите ответ, если вы сочтете его полезным.

Cedric Zoppolo 15.03.2018 21:18
Ответ принят как подходящий

Вы не поверите, но аксессор .str работает медленно. Вы можете использовать составление списков с большей производительностью.

df = pd.DataFrame({'col2':np.random.choice(['substring','midstring','nostring','substrate'],100000)})

Проверка на равенство

all(df['col2'].str.contains('substr', case=True, regex=False) ==
    pd.Series(['substr' in i for i in df['col2']]))

Выход:

True

Сроки:

%timeit df['col2'].str.contains('substr', case=True, regex=False)
10 loops, best of 3: 37.9 ms per loop

против

%timeit pd.Series(['substr' in i for i in df['col2']])
100 loops, best of 3: 19.1 ms per loop

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