Pandas фильтрация строк в таблице по двум столбцам из другой таблицы
Есть два df "длинный" и "короткий". Нужно отфильтровать строки в длинном df по значениям в коротком. Значения фильтруются по двум столбцам. Решил используя цикл for, но скорость очень низкая: длинна таблицы 80 000 строк, время порядка 8 минут. Фильтрация по str данным, длинна строки до 12 символов. Возможно кто-то подскажет, другие варианты решения. Пытался через map и dict - не получилось.
Условие:
import pandas as pd
# длинный df
columns = ['n1', 'n2']
data = [[1, 5],
[1, 0],
[9, 5],
[1, 1]]
df_long = pd.DataFrame(data=data, columns=columns)
df_long['tr'] = False
# короткий df
columns = ['n1', 'n2']
data = [[1, 5],
[1, 0],
[1, 1]]
df_short = pd.DataFrame(data=data, columns=columns)
# вариант решения
# сравниваем наличие n1 и n2, делаем пометку True
# n - номер строки i, ii - значения
for n, i, ii in zip(range(len(df_long)), df_long['n1'], df_long['n2']):
if len(df_short.loc[ (df_short['n1']==i) & (df_short['n2']==ii )]) == 1:
df_long.loc[n, 'tr'] = True
"Длинная"
n1 | n2 | tr |
---|---|---|
1 | 5 | False |
1 | 0 | False |
9 | 5 | False |
1 | 1 | False |
"Короткая"
n1 | n2 | None |
---|---|---|
1 | 5 | None |
1 | 0 | None |
1 | 1 | None |
"Решение"
n1 | n2 | tr |
---|---|---|
1 | 5 | True |
1 | 0 | True |
9 | 5 | False |
1 | 1 | True |
Ответы (3 шт):
Вам можно сделать left join - тогда те строки, которые не входят в правую таблицу получат в дополнительных столбцах значение NaN
:
import pandas as pd
# длинный df
columns = ['n1', 'n2']
data = [[1, 5],
[1, 0],
[9, 5],
[1, 1]]
df_long = pd.DataFrame(data=data, columns=columns)
# короткий df
columns = ['n1', 'n2']
data = [[1, 5],
[1, 0],
[1, 1],
[2, 1]]
df_short = pd.DataFrame(data=data, columns=columns)
df_short['tr'] = True
nf = pd.merge(
left=df_long,
right=df_short,
how='left',
on=['n1', 'n2'],
)
nf['tr'] = nf['tr'].fillna(value=False)
print(nf)
Обратите внимание, что я перенёс столбец tr
в df_short
и приравнял значения True
: при слиянии таблиц этот столбец будет добавлен в итоговую таблицу, причём для строк, которые во вторую таблицу не входят, там будет значение NaN
.
Результат
n1 n2 tr
0 1 5 True
1 1 0 True
2 9 5 False
3 1 1 True
~ в виде добавления к ответу @Pak Uula.
Колонку tr
можно вообще не создавать ни в одном фрейме, а воспользоваться параметром indicator
функции merge
:
df = pd.merge(df_long, df_short, on=['n1','n2'], how='left', indicator='tr')
df["tr"] = df["tr"]=="both"
df:
n1 n2 tr
0 1 5 True
1 1 0 True
2 9 5 False
3 1 1 True
Попробуйте такое решение:
df_long = df_long.query('n1 in @df_short.n1 and n2 in @df_short.n2')
@ — это специальный символ в query(), который позволяет ссылаться на столбцы DataFrame.
Я часто использую данное решение при работе с таблицами и анализе данных. Производительность не страдает.