У меня есть 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)
но они не быстрее. Второй оператор является логическим и очень быстрым. Как я могу ускорить первую выписку?






Вы можете ускорить это с помощью:
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, а не совпадения в первых рядах n. Я найду способ улучшить тайминги, если это вам поможет. Может быть, ответ @ScottBoston - довольно хорошее решение
Имейте в виду, что ваше решение также возвращает совпадения в первых строках n.
Верно. Действительно, ваше «другое» решение возвращает первые n совпадений, но это медленнее, чем при полном отсутствии .head(), т.е. без ограничения поиска.
Пожалуйста, посмотрите мое обновление. Я считаю, что «полное решение» - это довольно хороший подход.
Пожалуйста, проголосуйте за и / или примите ответ, если вы сочтете его полезным.
Вы не поверите, но аксессор .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
Это не совсем ответ на мой вопрос. Я скептически относился к ограничению пространства поиска: это, очевидно, повысит производительность, но за счет результатов. Однако, попробовав ваше «более быстрое» решение с n = 10000, результаты неплохие, а время значительно улучшилось. Но, в конце концов, я не могу развернуть это «более быстрое» решение, потому что оно предполагает совпадение в пределах первых результатов п, что может быть неверным! Я отредактирую свой вопрос, чтобы прояснить это.