May 5, 2021

Рекурсивный парсинг строчного поля

Рассмотрим, как эффективно распарсить строчное поле средствами библиотеки Pandas. Я для этого использую рекурсивный подход.

Его суть в следующей последовательности шагов:

  1. создаем копию анализируемой таблицы (пусть исходная - df, а копия - df_left, после станет понятно ее предназначение)
  2. проводим обзор шаблонов в df_left
  3. выбираем шаблон, который хотим заменить на некое значение, проводим действие с копией таблицы df_left (называю df_copy) и удостоверяемся в корректности операции
  4. применяем действие к исходной таблице df (или откладываем для применения всех операций вместе после алгоритма)
  5. удаляем шаблон из df_left и повторяем действия от второго шага

Рассмотрим на примере. Предположим, мы хотим распарсить поле days из массива, описывающего дни работы офисов вашей организации:

Обзор шаблонов удобно провести с помощью метода Series под названием value_counts:

df_left = df.copy()
df_left['days'].value_counts()

Пусть первый шаблон, с которым мы захотели разобраться, - это поля вида "-". Требуется заменить его на значение None для последующей обработки вместе с пропущенными значениями. Заводим копию таблицы df_left, проводим действие с ней, а затем проверяем корректность с помощью функции get_diffs_data_cols (подробнее рассказывал ранее):

df_copy = df_left.copy()
df_copy['days'] = df_copy['days'].replace({'-': None})
a = general.df_check_info.get_diffs_data_cols(df_left.drop_duplicates(subset='ids'),
                                              df_copy.drop_duplicates(subset='ids'), ['ids', 'days'], 'days')

Убедившись в корректности операции, применяем ее к исходной таблице df и удаляем шаблон из df_left:

df['days'] = df['days'].replace({'-': None})
df_left = df_left[df_left['days']!='-']

Далее опять проводим обзор шаблонов, выбираем конкретный, реализуем операцию на копии и выводим разницу для проверки корректности, например, так:

pats = df_left['days'].value_counts()

df_copy = df_left.copy()
df_copy['days'] = df_copy['days'].replace(r',+', r'.', regex=True)
df_copy['days'] = df_copy['days'].replace(r'\.{2,}', r'.', regex=True)

a = general.df_check_info.get_diffs_data_cols(df_left.drop_duplicates(subset='ids'),
                                              df_copy.drop_duplicates(subset='ids'), ['ids', 'days'], 'days')

Вновь применяем операцию к df и убираем шаблон из df_left:

df['days'] = df['days'].replace(r',+', r'.', regex=True)
df['days'] = df['days'].replace(r'\.{2,}', r'.', regex=True)

df_left['days'] = df_left['days'].replace(r',+', r'.', regex=True)
df_left['days'] = df_left['days'].replace(r'\.{2,}', r'.', regex=True)
df_left = df_left[~df_left['days'].str.contains(r'[А-Яа-я]{2}\.', na=False)]

Теперь множество оставшихся шаблонов примет вид:

pats = df_left['days'].value_counts()

Продолжаем рекурсивно применять операции до достижения желаемого результата.