Сравнение строк двух разных датафреймов pandas по нескольким условиям
Есть два датафрейма, нужно сравнить данные по нескольким условиям столбцов в нужной строке и вписать данные одного датафрейма в другой. Я решил это с помощью вложенных циклов
for i in range(df_mi.shape[0]):
for j in range(df_pz.shape[0]):
if (df_mi.iloc[i].A == df_pz.iloc[j].A) and (df_mi.iloc[i].C >= df_pz.iloc[j].C) and (df_mi.iloc[i].D <= df_pz.iloc[j].D) :
df_mi.iloc[i].new = df_pz.iloc[j].J
Но способ очень медленный. Есть ли идеи как сделать быстрее?
Ответы (2 шт):
Обычно в Pandas всё можно сделать без циклов. Вот вам пример приближенный к вашему:
import pandas as pd
import numpy as np
df1 = pd.DataFrame(np.random.rand(1000,5))
df2 = pd.DataFrame(np.random.rand(1000,5))
df1.columns = list('ABCDJ')
df2.columns = list('ABCDJ')
cond = (df1.C <= df2.C) & (df1.D >= df2.D)
df1.loc[cond, 'new'] = df1.loc[cond, 'J']
Основное, что нужно иметь в виду:
- Условия выборки обязательно нужно писать в скобках и соединять между собой через побитовые операции
&и|(а не через логическиеandиor), условий можно писать сколько угодно - В новый столбец данные можно записать только через индексную нотацию
['new'], а как вы написали через точку.newтак запись в новый столбец не работает, это сработает только если такой столбец уже есть, тогдаPandasсделает всё что нужно за вас и обеспечит доступ - Размер
Seriesслева и справа от знака присваивания должен быть одинаковый, поэтому я заранее создаю битовую маску, сохраняю её в переменную и использую.loc[]нотацию с этой маской с обеих сторон=
Судя по всему, вы хотите сравнить строки двух разных датафреймов и вписать данные одного датафрейма в другой, если выполнено несколько условий на столбцы. Один из способов ускорения вашего кода состоит в том, чтобы использовать метод apply для применения функции к каждой строке датафрейма. Этот метод обычно работает быстрее, чем циклы, так как он использует векторизованные операции вместо последовательных операций над каждым элементом.
Ниже приведен пример того, как это может выглядеть:
def compare_rows(row):
for i in range(df_pz.shape[0]):
if (row.A == df_pz.iloc[i].A) and (row.C >= df_pz.iloc[i].C) and (row.D <= df_pz.iloc[i].D):
return df_pz.iloc[i].J
return row.new
df_mi['new'] = df_mi.apply(compare_rows, axis=1)
Обратите внимание, что в функции compare_rows мы используем текущую строку датафрейма df_mi, которая передается в качестве аргумента функции. Мы сравниваем эту строку с каждой строкой датафрейма df_pz, и если все условия выполнены, мы возвращаем значение из столбца J датафрейма df_pz. Если ни одно из условий не выполнено, мы возвращаем значение из столбца new датафрейма df_mi.
Метод apply применяет функцию compare_rows к каждой строке датафрейма df_mi и возвращает результат в виде нового столбца new.
Этот код должен работать быстрее, чем ваш исходный код с вложенными циклами, так как он использует векторизованные операции вместо последовательных операций над каждым элементом.
Есть еще несколько способов, которые могут ускорить ваш код. Один из них состоит в том, чтобы использовать метод merge для слияния датафреймов df_mi и df_pz по нужным условиям. Этот метод обычно работает быстрее, чем циклы, так как он использует внутренние алгоритмы Pandas для слияния датафреймов.
Ниже приведен пример того, как это может выглядеть:
df_merged = df_mi.merge(df_pz, on=['A', 'C', 'D'], how='inner')
df_mi['new'] = df_merged['J']
Метод merge сливает датафреймы df_mi и df_pz по столбцам A, C и D, и возвращает результат в виде нового датафрейма df_merged. Затем мы присваиваем значения из столбца J датафрейма df_merged столбцу new датафрейма df_mi.