📊 Инструменты дата-журналиста #1: Jupyter Notebook и библиотека Pandas
В первой части серии публикаций мы разберемся с фильтрацией, сортировкой и очисткой датафреймов. Займемся декодированием текста с помощью библиотеки ftfy и определением языка через библиотеку google_trans_new. Блокнот Jupyter и наша шпаргалка по pandas прилагаются.
Серия «Инстурменты дата-журналиста» состоит из двух публикаций. В первой мы будем фильтровать, сортировать и очищать датасеты, используя библиотеку pandas в Jupyter Notebook. Ссылки на блокнот и шпаргалку вы найдете в конце статьи. Во второй части напишем парсер и создадим с его помощью собственный датасет, а также научимся визуализировать данные.
Вместо содержания ловите шпаргалку по статье:
Установка pandas
Установим библиотеку pandas в Jupyter Notebook:
и импортируем ее:
1. Объекты Series и DataFrame
Объект Series – одномерные массив, состоящий из элементов и их индексов:
Объект DataFrame – многомерный массив, как таблица в Excel.
Здесь: telephone number, name – названия столбцов с соответствующими значениями 123456789 ..., John ....
2.Импорт файлов и просмотр содержимого
Импортируем датасет (оригинал) с популярными поисковыми запросами в Google с 2000 по 2020 год. При импорте csv-файла все данные сохраняются в датафрейм df:
Аналогично есть команды для импорта других файлов: read_excel() – импорт excel-файла. read_sql() – импорт базы данных SQL. read_json() – импорт json-файла. read_html() – создает из HTML-таблицы список объектов DataFrame.
Выведем содержимое датафрейма на экран:
Отображаются первые и последние пять строчек.
Чаще всего значения в датасетах разделяются запятой. Чтобы задать другой разделитель добавим параметр sep="". По умолчанию разделитель между столбцами – запятая.
В нашем случае получаем:
Посмотрим первые пять строчек датафрейма с помощью df.head(5):
последние 5 строчек:
и пять случайных строчек:
Узнаем количество строчек и столбцов:
Здесь: 26955 – количество строчек. 5 – количество столбцов.
Выведем на экран содержимое датафрейма:
и использованной памяти:
Иногда при импорте данных числа принимают тип объект и при обработке данных возникает ошибка. Поэтому проверим какие типы данных находятся в каждом столбце:
Здесь: object – строчка. int64 – целое число.
Часто встречаемые типы данных: float64 – число с плавающей точкой. datetime64 – дата и время. bool – значения True или False.
Узнаем названия всех столбцов:
Выведем значения только столбца query:
и значения нескольких столбцов:
Через срез отобразим первые двадцать значений столбца query:
В нашем датасете индексы являются числами. Это не очень удобно для понимания, как работает loc и iloc, поэтому создадим небольшой датафрейм с элементами типа object в виде индексов:
loc выдает строки и/или столбцы по нечисловому значению индекса:
iloc получает строки и/или столбцы по числовому индексу:
Вернемся к основному датафрейму:
Выведем первые пять строчек с помощью iloc:
Узнаем какой элемент записан в третьей строчке и пятом столбце:
Выведем значения всех строчек с помощью метода iterrows():
и с помощью iloc():
Отобразим все элементы столбца query:
иначе:
Выведем строчки со значением Consumer Brands в столбце Сategory:
3. Операции над строчками и столбцами
3.1. Объединение датафреймов
Создадим два датафрейма df1 и df2 и добавим столбцы из второго датафрейма в первый с помощью concat(). При этом индексы должны совпадать, иначе мы получим NaN:
Здесь: df1иdf2– первый и второй датафрейм соответственно.
Добавим строчки датафреймаdf2кdf1. Названия столбцов должны совпадать, иначе получимNaN:
Добавим строчки методом append():
3.2. Добавление строчек и столбцов
Для добавления строчки создадим словарь new_row с названиям столбцов ввиде ключей:
Чтобы добавить строчку в определенную позицию используем метод loc:
Здесь: loc[2]– вставляем строчку на место третьей строчки.
Следующая запись добавит новый столбецnew_columnсправа и установит все строки на значениеnew:
Можно использовать метод insert(), чтобы указать, где должен быть новый столбец. У первого столбца индекс 0:
3.3. Удаление строчек и столбцов
Удалим последнюю строчку под номером 26954 c помощью drop():
Здесь : year– название столбца 0– в датафрейме строчка имеетось = 0, столбец –1.
Удалим столбецyear:
3.4. Переименование столбцов
Чтобы переименовать столбец используем rename():
Здесь: location – текущее название столбца. new_location – новое название столбца.
3.5. Замена символов в строке
С помощью replace() заменим в столбце year значения c 2020 на 2021:
Здесь: replace()– заменяет значения. to_replace=2020– значение, подлежащее замене. value=2021– то, на что меняем.
Методreplace()выполнит замену в результате полного совпадения значения, то есть знак вопроса?просто так не удалить – нужно писать регулярное выражение (regex). Быстрее воспользоваться методомstr.replace(), который выполняет замену подстроки без regex. Заменим знак вопроса обоими методами.
Замена с помощью методаreplace()и регулярных выражений:
Здесь: r''– обозначение регулярного выражения. []– поиск только символа в скобках. ?– поиск знака вопроса.
Замена, используя методstr.replace():
Здесь: regex=False– знак вопроса?используется в регулярных выражениях, поэтому их нужно отключить, присвоив параметру regex значениеFalse. По умолчаниюTrue.
Также можно заменить значения, используя методmask():
3.6. Суммирование элементов
Просуммируем элементы в столбце year с помощью sum():
3.7. Копирование датафрейма
Создадим глубокую копию (изменение в оригинале не влияют на копию и наоборот) датафрейма с помощью copy():
Здесь: df.copy – создает независимую копию датафрейма. Если нужна зависимая копия, то параметру deep присвоим значение False: df.copy(deep=False). По умолчанию True.
4. Сортировка
Сейчас массив отсортирован по году, давайте отсортируем его по стране (location) в прямом алфавитном порядке с помощью sort_values():
и в обратном алфавитном порядке:
Здесь: ascending=False– сортировка в обратном алфавитном порядке. По умолчаниюTrue.
Отсортируем по двум столбцам:rankиcategory:
В этом случаем приоритет отдается столбцу rank, так как он записан первым.
Здесь: ascending=[False, False] – сортировка в обратном порядке по обоим столбцам.
5. Очистка данных.
5.1. Изменение типа данных
Чтобы изменить тип данных воспользуемся методом astype():
Здесь: astype()– меняет тип переменной на строчныйObject; на число с плавающей точкойfloat; на целые числаint32иint64. to_numeric()– меняет типа переменной наint64.
5.2. Удаление и замена NaN-значений
Добавим в начало датафрейма строчку с отсутствующими значениями NaN:
Узнаем, какие элементы имеют значение NaN с помощью метода isnull():
и наоборот методом notnull():
Удалим строчки с отсутствующими значениями, используя dropna():
Удалим столбцы с отсутствующими значениями:
Заменим NaN на abcd методом fillna():
5.3. Удаление лишних пробелов
Добавим строчку, в которой есть дефектные элементы с лишними пробелами в конце и в начале:
Применим метод strip(), который удаляет символы (по умолчанию – пробел) с левого и правого края строки.
Здесь: map()– применяет к каждому элементу столбцовlocationиqueryметодstrip().
Напишем цикл, который проходится по всем элементам датафрейма и удаляет у строчек лишние пробелы слева и справа:
Здесь: new_row1иnew_row2– добавляемые строчки. if df[column].dtype == object– проверяем, является ли переменная объектом.
Проверить тип можно также через модульis_string_dtype:
Здесь:
is_string_dtype()– проверяет, является ли элемент строкой и возвращаетTrueилиFalse. Проверить на наличие числа можно черезis_numeric_dtype().
5.4. Обработка дат
Откроем сокращенный датасет (оригинал) c данными о погоде в Австралии:
Узнаем типы данных:
Как видно, элементы столбца Date – строчки. Поменяем их тип на дату методом to_datetime():
Добавим отдельно столбцы год, месяц, день из столбца Date методом DatetimeIndex():
Обратим внимание, что DatetimeIndex.weekday записывает не дату а индексы дней от 0 до 6: понедельник имеет значение 0, воскресенье – 6.
Другой способ записи:
6. Фильтрация и поиск
С помощью оператора сравнения >= получим все поисковые запросы c 2018 года включительно до 2020 года:
Более сложные выражения записываются с побитовыми операторами. В данном случае с оператором & (И, AND):
6.1. Метод startswith()
Проверим, совпадает ли начало каждого элемента строки с шаблоном G, используя метод startwith():
6.2. Метод endswith()
Метод endswith() проверяет совпадает ли конец каждого элемента строки с шаблоном a:
Методы startwith() и endswith() не поддерживают регулярные выражения. Для более точного поиска рассмотрим следующие методы.
6.3. Метод match()
Метод match() определяет, начинается ли каждая строка с шаблона, возвращая булево значение. Выведем все запросы о моделях Nokia:
Чтобы вывести больше информации заключим предыдущую запись в df[]:
В запросах попадается название компании Nokia без модели, например, Nokia 8. Уточним запрос, воспользовавшись регулярным выражением:
Здесь: import re – импорт библиотеки регулярных выражений. match() – ищет совпадение в начале строки. \b – обозначает границу слова. В нашем случае: слева – ничего, справа – буква. . – любой один символ, кроме символа переноса строки. [k][i][a] – только символы k, i и a соответственно. \s – любой пробельный символ. case=True – чувствителен к регистру.
6.4. Метод findall()
Метод findall возвращает совпадения шаблонов:
Здесь: df['findall'] != '']– отбирает все значения, за исключением пустой строчки. flags=re.IGNORECASE– игнорирует регистр при поиске. transform(''.join)– по умолчаниюfindall()заключает все запросы в квадратные скобки[], что не очень удобно для последующей обработки. Поэтому мы заменили скобки на пустое место''.
Напишем цикл, который выводит отфильтрованные запросы:
6.5. Метод str.contains()
Метод str.contains() в результате поиска возвращает булево значение:
6.6. Метод extractall()
Метод extraclall() вернет столбец с поисковым шаблоном. Результаты, не имеющие совпадений, отображаться не будут:
Присвоить название столбцу с результатами можно через следующую запись: ?P<название столбца>.
Несколько фильтров в одном запросе:
6.7. Метод isin()
Узнаем, содержатся ли искомые значения в столбце методом isin() :
7. Фильтрация по языку
7.1. Декодер
В датасете могут оказаться кракозябры: ЧЎЧЁЧЧ™Чќ. Для борьбы с ними есть отличная библиотека ftfy (англ. fixes text for you – исправляет текст для тебя), которая также имеет веб-реализацию. Установим ftfy:
Здесь:
apply(fix_encoding)– методapply()применяет к каждому элементу столбцаencodeфункциюfix_encoding.
7.2. Определение языка
Библиотека google_trans оказалась с багами, поэтому воспользуемся ее работоспособным форком – google_trans_new. Установим google_trans_new:
Здесь: for index, row ...– цикл по индексу и значению в столбцеcategory. try .... detector.detect(row)[1] – определение языка. 0 – сокращенный вариант en, 1– полный вариант english. except ... 'Error' – если по какой-то причине нам не удастся определить язык, в ячейку запишется значение Error.
Получим строки, содержащие язык russian:
8. Статистические данные
8.1. Метод describe()
Для просмотра статистической сводки столбцов, содержащих численные значения, введем df.describe().
Здесь: count– считает количество записей в столбце без значенияNaN. Отдельно можно вызвать черезdf.count(). mean – среднее значение df.mean(). std – стандартное отклонение df.std(). min – минимальное число в столбце df.min(). 25% – 25-й процентиль. 50%– 50-й процентиль. 75%– 75-й процентиль. max – максимальное число в столбцеdf.max().
8.2. Подсчет повторяющихся значений
Подсчитаем количество элементов в столбце query методом count():
С помощью метода value_counts узнаем сколько раз значения повторяются в столбце:
По умолчанию из результата исключаются NaN-значения. Чтобы отобразить их, поставим параметру dropna значение False.
Создадим столбец query_count, в который запишем количество упоминаний элемента:
Здесь: groupby()– используется для разделения данных на группы по критериям. Подробнее в документации. transform('count')– считает сколько раз повторяется значение в столбцеquery.
Выведем уникальные значения в столбцеlocationчерез методunique():
9. Сохранение датафрейма
Сохраним датафрейм в csv-файл:
Здесь: save_file – столбцы датафрейма. to_csv() – сохранить датафрейм в файл формата .csv.
Откроем сохраненный файл:
После сохранения файла у нас повявился столбец Unnamed: 0. Он нам не нужен, потому удалим его: