Pandas решение проблемы SettingWithCopyWarning
Описание датасетов
Датасет 1 (dataset-users_emails) — USER_ID, EMAIL. В нем USER_ID пользователя из CRM и EMAIL (далее будет выступать как ключ связки).
Датасет 2 (dataset-emails_phones) — EMAIL, PHONE. Это набор данных, которые мы собирали из различных источников, целевой колонкой для бизнеса номер телефона.
Задача — объеденить два датасета, импортировать полученный датасет в CRM и далее контакт центр должен прозвонить этих клиентов.
Подготовка — перед решением задачи нужно обработать датасет 2 так как в нем телефоны были все в разнобой. Основная подготовка — это привести телефон в один формат — 380XXXXXXXXX.
Импорт библиотек и чтение датасетов:
import pandas as pd import numpy as np import re df_users = pd.read_csv('dataset-users_emails.csv') df_phones = pd.read_csv('dataset-emails_phones.csv')
Очистка телефонов от всего лишнего при помощи библиотеки re:
df_phones['PHONE'] = df_phones['PHONE'].map(lambda x: re.sub(r'\W+', '', x))
Когда телефоны очищены от спец-символов, пробелов и прочих знаков нужно понять телефоны валидны или нет. Что-то очень сложное с валидацией я не стал проворачивать, а просто проверил на кол-во символов при помощи np.where и если телефон не валиден в колонку is_valid вставил 1, если нет 0:
df['is_valid'] = np.where(df['PHONE'].str.len() == 12, 1, 0)
Далее нужно определить сколько валидных телефонов, а сколько нет. Для этого использовал просто groupby:
Далее, нужно было обработать невадидные телефоны.
Я предварительно изучил какие там форматы и что мне нужно было сделать)
Основная проблема с телефонами была в том, что там были 8 в начале строк, или же не хваталов 380. Дай думаю легко и просто выполню команду и почищу данные:
df_phones[ (df_phones['is_valid'] == 0) & (df_phones['PHONE'].str.get(0) == '8') ]['PHONE'] = df_phones['PHONE'].str.replace('8','')
Выполнив команду, получил ошибку (или предупреждение) SettingWithCopyWarning:
/var/folders/qt/mx5djmn92sv0v_d1lll28ffm0000gn/T/ipykernel_32045/633074573.py:1: SettingWithCopyWarning: A value is trying to be set on a copy of a slice from a DataFrame. Try using .loc[row_indexer,col_indexer] = value instead See the caveats in the documentation: https://pandas.pydata.org/pandas-docs/stable/user_guide/indexing.html#returning-a-view-versus-a-copy
Это предупреждение возникает, когда мы пытаемся выполнить присваивание с использованием цепной индексации, поскольку цепная индексация имеет непредсказуемые результаты. Решение проблемы я нашел в этой статье, если вам интересно то можете изучить подробнее.
Для решения проблемы SettingWithCopyWarning можно испольовать функцию pandas — .loc:
Здесь я удалил лишнюю 8 сначала строки, чтобы у меня телефон получился в формате XXXXXXXXXX
df_phones.loc[(df_phones["is_valid"] == 0 ) & (df_phones['PHONE'].str.get(0) == '8'), 'PHONE'] = df_phones['PHONE'].str.replace('8','')
Далее таким-же способом добавляются 38 к строке.
df_phones.loc[df_phones["is_valid"] == 0, "PHONE"] = '38' + df_phones['PHONE']
И еще раз функция валидации (код выше). Из оставшегося массива все что не валидно — обрабатывается руками или придумается другой способ 🙂
На этом у меня все. Если у вас будут вопросы, пишите с радостью обсудил бы 🙂