#python-3.x #pandas #dataframe
#python-3.x #pandas #фрейм данных
Вопрос:
У меня есть следующий фрейм данных
top bottom fontname size x0 x1 text
0 62.890 73.890 HNGMRP HelveticaNeueLTStd-Bd 11.000 321.730 520.115 RISK MANAGEMENT AND INTERNAL
1 76.893 87.893 HNGMRP HelveticaNeueLTStd-Bd 11.000 321.730 376.334 CONTROL
2 146.897 157.897 HNGMRP HelveticaNeueLTStd-Bd 11.000 76.535 203.662 COMPANY SECRETARY
3 272.913 283.913 HNGMRP HelveticaNeueLTStd-Bd 11.000 76.535 222.593 INDEPENDENT AUDITORS
4 286.916 297.916 HNGMRP HelveticaNeueLTStd-Bd 11.000 76.535 167.164 REMUNERATION
Я хочу
- объедините текст
row[i].text
иrow[i 1].text
, еслиabs(row[i].bottom - row[i 1].top) < row[i].size
- заменить
joined_text
наrow[i].text
- заменить
row[i].bottom
наrow[i 1].bottom
- отбросить
row[i 1]
Например:
row 0
имеетtext
:RISK MANAGEMENT AND INTERNAL
row 1
имеетtext
:CONTROL
row 0
иrow 1
оба имеют одинаковоеsize
значение: 11abs(row[0].bottom - row[1].top)
равно 3,003
Потому что 3.003 < 11
- желаемый
row[0].text
являетсяRISK MANAGEMENT AND INTERNAL CONTROL
- желаемый
row[0].bottom
является87.893
row[1]
удаляется из фрейма данных
Для наглядности желаемый результат следующий:
top bottom fontname size x0 x1 text
0 62.890 87.893 HNGMRP HelveticaNeueLTStd-Bd 11.000 321.730 520.115 RISK MANAGEMENT AND INTERNAL CONTROL
1 146.897 157.897 HNGMRP HelveticaNeueLTStd-Bd 11.000 76.535 203.662 COMPANY SECRETARY
2 272.913 297.916 HNGMRP HelveticaNeueLTStd-Bd 11.000 76.535 222.593 INDEPENDENT AUDITORS REMUNERATION
Это то, что я пытался:
def df_section_text(self) -> pd.DataFrame:
df_title_text = self.df_title_text
df_next_title_text = self.df_title_text.shift(-1).dropna()
df_section_text = pd.DataFrame()
for next_title, title in zip(df_next_title_text.itertuples(index=False), df_title_text.itertuples(index=False)):
diff_btw_titles = abs(title.bottom - next_title.top)
if diff_btw_titles < title.size:
title = pd.DataFrame([title]).to_dict()
title['bottom'][0] = next_title.bottom
title['text'][0] = next_title.text
title = pd.DataFrame.from_dict(title)
df_section_text = df_section_text.append([title])
df_section_text = df_section_text.drop_duplicates(subset=['bottom']).reset_index()
return df_section_text
где self.df_title_text
находится проблемный фрейм данных, представленный выше.
Это происходит медленно, когда увеличивается номер строки. Есть ли другой более быстрый и элегантный способ получения желаемого результата? Спасибо.
Ответ №1:
Давайте попробуем использовать shift
с cumcount
, чтобы получить подгруппу, тогда нам просто нужно на основе этого ключа сделать groupby
с agg
s = (df['bottom'].shift()-df['top']).abs().gt(df['size']).cumsum()
out = df.groupby(s).agg({'top':'first',
'bottom':'last',
'fontname':'first',
'size':'first',
'x0':'first',
'x1':'first',
'text':' '.join})
out
Out[20]:
top bottom ... x1 text
0 62.890 87.893 ... 520.115 RISKMANAGEMENTANDINTERNAL CONTROL
1 146.897 157.897 ... 203.662 COMPANYSECRETARY
2 272.913 297.916 ... 222.593 INDEPENDENTAUDITORS REMUNERATION
[3 rows x 7 columns]
Комментарии:
1. Это отличный ответ. Кроме того, поскольку тип
top
иbottom
являетсяDecimal
:s = ((df['bottom'].shift() - df['top']).abs() > df['size']).cumsum()
, это позволит избежать ошибкиdecimal.InvalidOperation: [<class 'decimal.InvalidOperation'>]
.