У меня есть следующий df:
df = pd.DataFrame({'ID1':[1,2,3,4,5,6],'ID2':[2,6,6,2,1,2],'AREA':[1,1,1,1,1,1]})
...
ID1 ID2 AREA
0 1 2 1
1 2 6 1
2 3 6 1
3 4 2 1
4 5 1 1
5 6 2 1
Я аккумулирую столбец AREA так:
for id_ in df.ID1:
id1_filter = df.ID1 == id_
id2_filter = (df.ID1 == id_) | (df.ID2 == id_)
df.loc[id1_filter, 'AREA'] = df.loc[id2_filter].AREA.sum()
print(df)
...
ID1 ID2 AREA
0 1 2 2
1 2 6 5
2 3 6 1
3 4 2 1
4 5 1 1
5 6 2 7
Для каждого id_
в ID1
AREA
суммируется, где ID1
== id_
или ID2 == id_
, и он всегда запускается, когда df
сортируется по ID1
.
Реальный фреймворк данных, над которым я работаю, составляет 150 000 записей, каждая строка которых принадлежит уникальному ID1. Выполнение вышеуказанного на этом кадре данных занимает 2,5 часа. Поскольку эта операция будет проходить неоднократно в обозримом будущем, я решил сохранить индексы True значений в id1_filter
и id2_filter
в БД со следующей схемой.
Таблица ID1:
ID_,INDEX_
1 , 0
2 , 1
etc, ect
Таблица ID2:
ID_,INDEX_
1 , 0
1 , 4
2 , 0
2 , 1
2 , 3
2 , 5
etc, etc
В следующий раз, когда я запустил накопление в столбце AREA
(теперь заполненное разными значениями AREA
), я прочитал в таблицах sql и конвертировал их в dicts. Затем я использую эти dicts для захвата записей, которые мне нужны во время цикла суммирования.
id1_dict = pd.read_sql('select * from ID1',db_engine).groupby('ID_').INDEX_.unique().to_dict()
id2_dict = pd.read_sql('select * from ID2',db_engine).groupby('ID_').INDEX_.unique().to_dict()
# print indices for id1_filter and id2_fillter for id 1
print(id1_dict[1])
print(id2_dict[1])
...
[0]
[0, 4]
for id_ in df.ID1:
df.loc[id1_dict[id_], 'AREA'] = df.loc[id2_dict[id_]].AREA.sum()
При работе таким образом это занимает всего 6 минут!
Мой вопрос: есть ли лучший/стандартный способ справиться с этим сценарием, т.е. Сохранить выбор данных для дальнейшего использования? Боковое замечание. Я установил индекс в столбцах идентификатора таблицы SQL и попытался получить индексы, запросив таблицу для каждого идентификатора, и он работает хорошо, но все равно занимает немного больше, чем выше (9 минут).
Один из способов сделать это:
df = df.set_index('ID1')
for row in df.join(df.groupby('ID2')['AREA'].apply(lambda x: x.index.tolist()),rsuffix='_').dropna().itertuples():
df.loc[row[0],'AREA'] += df.loc[row[3],'AREA'].sum()
df = df.reset_index()
и вы получите ожидаемый результат
ID1 ID2 AREA
0 1 2 2
1 2 6 5
2 3 6 1
3 4 2 1
4 5 1 1
5 6 2 7
Теперь на более df
например:
df = pd.DataFrame( {'ID1':range(1,1501),'ID2': np.random.randint(1,1501,(1500,)),'AREA':[1]*1500},
columns = ['ID1','ID2','AREA'])
Представленный здесь метод поворачивается примерно на 0,76 с на моем компьютере, в то время как ваш первый работает в 6.5 с.
В конечном итоге вы можете создать df_list
например:
df_list = (df.set_index('ID1')
.join(df.set_index('ID1').groupby('ID2')['AREA']
.apply(lambda x: x.index.tolist()),rsuffix='_ID2')
.dropna().drop(['AREA','ID2'],1))
сохранить где-нибудь информацию, связанную с ID1 и ID2: здесь вы можете увидеть, что идентификатор равен 2 в столбце ID2, где значение ID1 = 1, 4 и 6
AREA_ID2
ID1
1 [5]
2 [1, 4, 6]
6 [2, 3]
а затем вы можете запустить, чтобы не воссоздать df_list
с небольшой разницей в коде:
df = df.set_index('ID1')
for row in df_list.itertuples():
df.loc[row[0],'AREA'] += df.loc[row[1],'AREA'].sum()
df = df.reset_index()
Надеюсь, что это быстрее