Данные по нескольким условиям из столбцов контрольных точек
У меня возникла проблема со скоростью обработки своих данных. Есть датафрейм из трех блоков: наименование, признаки, контрольные точки. И проще говоря мне нужно собрать наименования с признаками в одну таблицу по всем контрольным точкам построчно. Значения контрольных точек указывают какие признаки выбрать для каждого наименования!
Я использовал конкатенацию с циклом. И это оказалось слишком медленным для меня. Около 20-30 секунд. И я чувствую, что его можно ускорить, но я не знаю, как. Мне нужно, чтобы это заняло 2-3 секунды. Скажите, пожалуйста, я неправильно выбрал метод индексации поиска признаков по значениям всех контрольных точек? Как я могу улучшить или коренным образом изменить код?
Вот описание датафрейма:
- Наименование ('index', 'name', 'country')
- Различные признаки по группам 'c', 'd' ('a1_b1_c_foo', 'a1_b1_c_foo_bar', ..., 'a1_b1_d_foo_bar_baz', ...)
- Несколько контрольных точек к каждому наименованию ('point_1' 'point_1_description', ..., 'point_2', ...). Каждый чекпоинт содержит уточняющие значения для извлечения конкретных столбцов признаков ('point_1_a', point_1_b', ....)
Я привел упрощенный датафрейм в пример. Чтобы понять приблизительный объем данных я напишу сколько будет содержать настоящий датафрейм:
- Десятки тысяч строк (наименований)
- Пятнадцать признаков группы 'c', десять признаков группы 'd'
- 'a1_ ...' 'a2_ ...' планируется от 1 до 15
- 'b1 ...' 'b2 ...' планируется от 1 до 3
- 40 контрольных точек
index | name | country | a1_b1_c_foo | a1_b1_c_foo_bar | a1_b1_d_foo_bar_baz | a2_b1_c_foo | a2_b1_c_foo_bar | a2_b1_d_foo_bar_baz | a1_b2_c_foo | a1_b2_c_foo_bar | a1_b2_d_foo_bar_baz | a2_b2_c_foo | a2_b2_c_foo_bar | a2_b2_d_foo_bar_baz | a1_b3_c_foo | a1_b3_c_foo_bar | a1_b3_d_foo_bar_baz | a2_b3_c_foo | a2_b3_c_foo_bar | a2_b3_d_foo_bar_baz | point_1 | point_1_description | point_1_a | point_1_b | point_2 | point_2_description | point_2_a | point_2_b | point_3 | point_3_description | point_3_a | point_3_b | point_4 | point_4_description | point_4_a | point_4_b |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
0 | Bernard | France | value1 | value2 | value3 | value4 | value5 | value6 | value7 | value8 | value9 | value10 | value11 | value12 | value13 | value14 | value15 | value16 | value17 | value18 | name1 | description1 | 1 | 1 | name2 | description2 | 2 | 1 | name3 | description3 | 1 | 2 | name4 | description4 | 1 | 2 |
1 | Elon | USA | value19 | value20 | value21 | value22 | value23 | value24 | value25 | value26 | value27 | value28 | value29 | value30 | value31 | value32 | value33 | value34 | value35 | value36 | name5 | description5 | 1 | 2 | name6 | description6 | 1 | 2 | name8 | description8 | 2 | 2 | ||||
2 | Jeff | USA | value37 | value38 | value39 | value40 | value41 | value42 | value43 | value44 | value45 | value46 | value47 | value48 | value49 | value50 | value51 | value52 | value53 | value54 | name9 | description9 | 2 | 1 | name11 | description11 | 2 | 2 | name12 | description12 | 1 | 1 | ||||
3 | Lawrence | USA | value55 | value56 | value57 | value58 | value59 | value60 | value61 | value62 | value63 | value64 | value65 | value66 | value67 | value68 | value69 | value70 | value71 | value72 | name13 | description13 | 1 | 2 | name14 | description14 | 1 | 1 | name16 | description16 | 1 | 2 |
В итоговой таблице то что я хочу получить: собраны все контрольные точки с соответствующими признаками, которые определяются в зависимости от значения столбцов 'point_...a', 'point...b'.
Пример итоговой таблицы:
name | country | c_foo | c_foo_bar | d_foo_bar_baz | point | point_description | point_a | point_b | |
---|---|---|---|---|---|---|---|---|---|
0 | Bernard | France | value1 | value2 | value9 | name1 | description1 | 1 | 1 |
1 | Elon | USA | value25 | value26 | value33 | name5 | description5 | 1 | 2 |
2 | Jeff | USA | value40 | value41 | value48 | name9 | description9 | 2 | 1 |
3 | Lawrence | USA | value61 | value62 | value69 | name13 | description13 | 1 | 2 |
0 | Bernard | France | value4 | value5 | value12 | name2 | description2 | 2 | 1 |
1 | Elon | USA | value25 | value26 | value33 | name6 | description6 | 1 | 2 |
2 | Lawrence | USA | value55 | value56 | value63 | name14 | description14 | 1 | 1 |
0 | Bernard | France | value7 | value8 | value15 | name3 | description3 | 1 | 2 |
0 | Bernard | France | value7 | value8 | value15 | name4 | description4 | 1 | 2 |
Вот мой код:
import pandas as pd
import numpy as np
with open('test_r.csv', encoding='utf-8') as f:
df = pd.read_csv(f, sep=' ', low_memory=False)
collist_tmp = [
"a1_b1_c_foo",
"a1_b1_c_foo_bar",
"a1_b1_d_foo_bar_baz",
"a2_b1_c_foo",
"a2_b1_c_foo_bar",
"a2_b1_d_foo_bar_baz",
"a1_b2_c_foo",
"a1_b2_c_foo_bar",
"a1_b2_d_foo_bar_baz",
"a2_b2_c_foo",
"a2_b2_c_foo_bar",
"a2_b2_d_foo_bar_baz",
"a1_b3_c_foo",
"a1_b3_c_foo_bar",
"a1_b3_d_foo_bar_baz",
"a2_b3_c_foo",
"a2_b3_c_foo_bar",
"a2_b3_d_foo_bar_baz",
]
collist_d = [
"d_foo_bar_baz",
]
collist_final_df = [
"name",
"country",
"c_foo",
"c_foo_bar",
"d_foo_bar_baz",
'point',
'point_description',
'point_a',
'point_b',
]
# assembly checkpoints "point"
list_dfs = []
# loop
for i in range(1,5):
collist_c = [
"name",
"country",
"c_foo",
"c_foo_bar",
'point_' + str(i),
'point_' + str(i) + '_description',
'point_' + str(i) + '_a',
'point_' + str(i) + '_b',
]
collist_df_all = [
"name",
"country",
"c_foo",
"c_foo_bar",
"d_foo_bar_baz",
'point_' + str(i),
'point_' + str(i) + '_description',
'point_' + str(i) + '_a',
'point_' + str(i) + '_b',
]
df = df.loc[~df[f'point_{i}'].isnull()]
# tmp
tmp = df.reindex(columns=collist_tmp)
tmp.columns = pd.MultiIndex.from_frame(tmp.columns.str.extract('(a[^_]+_b[^_]+)_(.*)'))
# group c
target_c = 'a'+ (pd.Series(df[f'point_{i}_a'].astype('Int16') + 0, dtype='string')) + '_b'+ (df[f'point_{i}_b'].astype('Int16').astype(str))
df_c = (df
.reset_index()
.merge(tmp.stack(level=0, dropna=False), left_on=['index', target_c], right_index=True)
.set_index('index')[collist_c]
.reset_index()
)
# group d
target_d = 'a'+ (df[f'point_{i}_a'].astype('Int16').astype(str)) + '_b'+ (pd.Series(df[f'point_{i}_b'].astype('Int16') + 1, dtype='string'))
df_d = (df
.reset_index()
.merge(tmp.stack(level=0, dropna=False), left_on=['index', target_d], right_index=True)
.set_index('index')[collist_d]
.reset_index()
)
# assembly df with group c and group d
df_all = pd.concat(
objs=(iDF.set_index('index') for iDF in (df_c, df_d)),
axis=1,
join='inner'
).reset_index().drop(columns=['index']).reindex(columns=collist_df_all)
df_all.columns = collist_final_df
# assembly checkpoints "point"
list_dfs.append(df_all)
# assembly checkpoints "point"
df_final = pd.concat(list_dfs)
print(df_final)