В csv файле меньше колонок чем ожидается найдено 5 колонок ожидается 9
Язвы и грабли CSV и Excel: проблемы и решения
CSV является стандартом де-факто для связи между собой разнородных систем, для передачи и обработки объемных данных с «жесткой», табличной структурой. Во многих скриптовых языках программирования есть встроенные средства разбора и генерации, он хорошо понятен как программистам, так и рядовым пользователям, а проблемы с самими данными в нем хорошо обнаруживаются, как говорится, на глаз.
История этого формата насчитывает не менее 30 лет. Но даже сейчас, в эпоху повального использования XML, для выгрузки и загрузки больших объемов данных по-прежнему используют CSV. И, несмотря на то, что сам формат довольно неплохо описан в RFC, каждый его понимает по-своему.
Начнем с того, что форматом CSV на самом деле называют три разных текстовых формата, отличающихся символами-разделителями: собственно сам CSV (comma-separated values — значения, разделенные запятыми), TSV (tab-separated values — значения, разделенные табуляциями) и SCSV (semicolon separated values — значения, разделенные точкой с запятой). В жизни все три могут называться одним CSV, символ-разделитель в лучшем случае выбирается при экспорте или импорте, а чаще его просто «зашивают» внутрь кода. Это создает массу проблем в попытке разобраться.
Как иллюстрацию возьмем казалось бы тривиальную задачу: импортировать в Microsoft Outlook данные из таблицы в Microsoft Excel.
В Microsoft Excel есть средства экспорта в CSV, а в Microsoft Outlook — соответствующие средства импорта. Что могло быть проще — сделал файлик, «скормил» почтовой программе и — дело сделано? Как бы не так.
Создадим в Excel тестовую табличку:
… и попробуем экспортировать ее в три текстовых формата:
«Текст Unicode» | Кодировка — UTF-16, разделители — табуляция, переводы строк — 0×0D, 0×0A, объем файла — 222 байт |
«CSV (разделители — запятые)» | Кодировка — Windows-1251, разделители — точка с запятой (не запятая!), во второй строке значение телефонов не взято в кавычки, несмотря на запятую, зато взято в кавычки значение «01;02», что правильно. Переводы строк — 0×0D, 0×0A. Объем файла — 110 байт |
«Текстовые файлы (с разделителями табуляции)» | Кодировка — Windows-1251, разделители — табуляция, переводы строк — 0×0D, 0×0A. Значение «01;02» помещено в кавычки (без особой нужды). Объем файла — 110 байт |
Какой вывод мы делаем из этого. То, что здесь Microsoft называет «CSV (разделители — запятые)», на самом деле является форматом с разделителями «точка с запятой». Формат у Microsoft — строго Windows-1251. Поэтому, если у вас в Excel есть Unicode-символы, они на выходе в CSV отобразятся в вопросительные знаки. Также то, что переводами строк является всегда пара символов, то, что Microsoft тупо берет в кавычки все, где видит точку с запятой. Также то, что если у вас нет Unicode-символов вообще, то можно сэкономить на объеме файла. Также то, что Unicode поддерживается только UTF-16, а не UTF-8, что было бы сильно логичнее.
«Значения, разделенные табуляцией(Windows)» | Скармливаем аутлуку файл tsv, с разделенными табуляцией значениями и. — чтобы вы думали. Outlook склеивает поля и табуляцию не замечает. Заменяем в файле табуляцию на запятые и, как видим, поля уже разбирает, молодец. |
«Значения, разделенные запятыми (Windows)» | А вот аутлук как раз понимает все верно. Comma — это запятая. Поэтому ожидает в качестве разделителя запятую. А у нас после экселя — точка с запятой. В итоге аутлук распознает все неверно. |
Два майкрософтовских продукта не понимают друг друга, у них напрочь отсутствует возможность передать через текстовый файл структурированные данные. Для того, чтобы все заработало, требуются «пляски с бубном» программиста.
Мы помним, что Microsoft Excel умеет работать с текстовыми файлами, импортировать данные из CSV, но в версии 2007 он делает это очень странно. Например, если просто открыть файл через меню, то он откроется без какого-либо распознавания формата, просто как текстовый файл, целиком помещенный в первую колонку. В случае, если сделать дабл-клик на CSV, Excel получает другую команду и импортирует CSV как надо, не задавая лишних вопросов. Третий вариант — вставка файла на текущий лист. В этом интерфейсе можно настраивать разделители, сразу же смотреть, что получилось. Но одно но: работает это плохо. Например, Excel при этом не понимает закавыченных переводов строк внутри полей.
Более того, одна и та же функция сохранения в CSV, вызванная через интерфейс и через макрос, работает по-разному. Вариант с макросом не смотрит в региональные настройки вообще.
Стандарта CSV как такового, к сожалению, нет, но, между тем, существует т.н. memo. Это RFC 4180 года, в котором описано все довольно толково. За неимением ничего большего, правильно придерживаться хотя бы RFC. Но для совместимости с Excel следует учесть его собенности.
Вот краткая выжимка рекомендаций RFC 4180 и мои комментарии в квадратных скобках:
Вот в нотации ABNF описание формата:
Также при реализации формата нужно помнить, что поскольку здесь нет указателей на число и тип колонок, поскольку нет требования обязательно размещать заголовок, здесь есть условности, о которых необходимо не забывать:
Пример валидного CSV, который можно использовать для тестов:
точно такой же SCSV:
Второй файлик, который по логике SCSV, экселом воспринимается и выходит вот что:
С приведением типов сработало, но зато теперь не обрабатываются нормально переводы строк и осталась проблема с ведущими нулями, кавычками и лишними пробелами. Да и пользователям так открывать CSV крайне неудобно.
Есть эффективный способ, как заставить Excel не приводить типы, когда это нам не нужно. Но это будет CSV «специально для Excel». Делается это помещением знака «=» перед кавычками везде, где потенциально может возникнуть проблема с типами. Заодно убираем лишние пробелы.
И вот что случаеся, если мы открываем этот файлик в экселе:
Чтобы построить хороший и удобный импортер CSV, необходимо помнить о следующем:
Редактируем CSV-файлы, чтобы не сломать данные
Продукты HFLabs в промышленных объемах обрабатывают данные: адреса, ФИО, реквизиты компаний и еще вагон всего. Естественно, тестировщики ежедневно с этими данными имеют дело: обновляют тест-кейсы, изучают результаты очистки. Часто заказчики дают «живую» базу, чтобы тестировщик настроил сервис под нее.
Первое, чему мы учим новых QA — сохранять данные в первозданном виде. Все по заветам: «Не навреди». В статье я расскажу, как аккуратно работать с CSV-файлами в Excel и Open Office. Советы помогут ничего не испортить, сохранить информацию после редактирования и в целом чувствовать себя увереннее.
Материал базовый, профессионалы совершенно точно заскучают.
Что такое CSV-файлы
Формат CSV используют, чтобы хранить таблицы в текстовых файлах. Данные очень часто упаковывают именно в таблицы, поэтому CSV-файлы очень популярны.
CSV-файл состоит из строк с данными и разделителей, которые обозначают границы столбцов
CSV расшифровывается как comma-separated values — «значения, разделенные запятыми». Но пусть название вас не обманет: разделителями столбцов в CSV-файле могут служить и точки с запятой, и знаки табуляции. Это все равно будет CSV-файл.
У CSV куча плюсов перед тем же форматом Excel: текстовые файлы просты как пуговица, открываются быстро, читаются на любом устройстве и в любой среде без дополнительных инструментов.
Из-за своих преимуществ CSV — сверхпопулярный формат обмена данными, хотя ему уже лет 40. CSV используют прикладные промышленные программы, в него выгружают данные из баз.
Одна беда — текстового редактора для работы с CSV мало. Еще ничего, если таблица простая: в первом поле ID одной длины, во втором дата одного формата, а в третьем какой-нибудь адрес. Но когда поля разной длины и их больше трех, начинаются мучения.
Следить за разделителями и столбцами — глаза сломаешь
Еще хуже с анализом данных — попробуй «Блокнотом» хотя бы сложить все числа в столбце. Я уж не говорю о красивых графиках.
Поэтому CSV-файлы анализируют и редактируют в Excel и аналогах: Open Office, LibreOffice и прочих.
Ветеранам, которые все же дочитали: ребята, мы знаем об анализе непосредственно в БД c помощью SQL, знаем о Tableau и Talend Open Studio. Это статья для начинающих, а на базовом уровне и небольшом объеме данных Excel с аналогами хватает.
Как Excel портит данные: из классики
Все бы ничего, но Excel, едва открыв CSV-файл, начинает свои лукавые выкрутасы. Он без спроса меняет данные так, что те приходят в негодность. Причем делает это совершенно незаметно. Из-за этого в свое время мы схватили ворох проблем.
Большинство казусов связано с тем, что программа без спроса преобразует строки с набором цифр в числа.
Округляет. Например, в исходной ячейке два телефона хранятся через запятую без пробелов: «5235834,5235835». Что сделает Excel? Лихо превратит номера́ в одно число и округлит до двух цифр после запятой: «5235834,52». Так мы потеряем второй телефон.
Приводит к экспоненциальной форме. Excel заботливо преобразует «123456789012345» в число «1,2E+15». Исходное значение потеряем напрочь.
Проблема актуальна для длинных, символов по пятнадцать, цифровых строк. Например, КЛАДР-кодов (это такой государственный идентификатор адресного объекта: го́рода, у́лицы, до́ма).
Удаляет лидирующие плюсы. Excel считает, что плюс в начале строки с цифрами — совершенно лишний символ. Мол, и так ясно, что число положительное, коль перед ним не стоит минус. Поэтому лидирующий плюс в номере «+74955235834» будет отброшен за ненадобностью — получится «74955235834». (В реальности номер пострадает еще сильнее, но для наглядности обойдусь плюсом).
Потеря плюса критична, например, если данные пойдут в стороннюю систему, а та при импорте жестко проверяет формат.
Разбивает по три цифры. Цифровую строку длиннее трех символов Excel, добрая душа, аккуратно разберет. Например, «8 495 5235834» превратит в «84 955 235 834».
Форматирование важно как минимум для телефонных номеров: пробелы отделяют коды страны и города от остального номера и друг от друга. Excel запросто нарушает правильное членение телефона.
Удаляет лидирующие нули. Строку «00523446» Excel превратит в «523446».
А в ИНН, например, первые две цифры — это код региона. Для Республики Алтай он начинается с нуля — «04». Без нуля смысл номера исказится, а проверку формата ИНН вообще не пройдет.
Меняет даты под локальные настройки. Excel с удовольствием исправит номер дома «1/2» на «01.фев». Потому что Windows подсказал, что в таком виде вам удобнее считывать даты.
Побеждаем порчу данных правильным импортом
Если серьезно, в бедах виноват не Excel целиком, а неочевидный способ импорта данных в программу.
По умолчанию Excel применяет к данным в загруженном CSV-файле тип «General» — общий. Из-за него программа распознает цифровые строки как числа. Такой порядок можно победить, используя встроенный инструмент импорта.
Запускаю встроенный в Excel механизм импорта. В меню это «Data → Get External Data → From Text».
Выбираю CSV-файл с данными, открывается диалог. В диалоге кликаю на тип файла Delimited (с разделителями). Кодировка — та, что в файле, обычно определяется автоматом. Если первая строка файла — шапка, отмечаю «My Data Has Headers».
Перехожу ко второму шагу диалога. Выбираю разделитель полей (обычно это точка с запятой — semicolon). Отключаю «Treat consecutive delimiters as one», а «Text qualifier» выставляю в «
На третьем шаге выбираю формат полей, ради него все и затевалось. Для всех столбцов выставляю тип «Text». Кстати, если кликнуть на первую колонку, зажать шифт и кликнуть на последнюю, выделятся сразу все столбцы. Удобно.
Дальше Excel спросит, куда вставлять данные из CSV — можно просто нажать «OK», и данные появятся в открытом листе.
Перед импортом придется создать в Excel новый workbook
Но! Если я планирую добавлять данные в CSV через Excel, придется сделать еще кое-что.
После импорта нужно принудительно привести все-все ячейки на листе к формату «Text». Иначе новые поля приобретут все тот же тип «General».
После этого, если повезет, Excel оставит исходные данные в покое. Но это не самая твердая гарантия, поэтому мы после сохранения обязательно проверяем файл через текстовый просмотрщик.
Альтернатива: Open Office Calc
Для работы с CSV-файлами я использую именно Calc. Он не то чтобы совсем не считает цифровые данные строками, но хотя бы не применяет к ним переформатирование в соответствии с региональными настройками Windows. Да и импорт попроще.
Конечно, понадобится пакет Open Office (OO). При установке он предложит переназначить на себя файлы MS Office. Не рекомендую: хоть OO достаточно функционален, он не до конца понимает хитрое микрософтовское форматирование документов.
А вот назначить OO программой по умолчанию для CSV-файлов — вполне разумно. Сделать это можно после установки пакета.
Итак, запускаем импорт данных из CSV. После двойного клика на файле Open Office показывает диалог.
Заметьте, в OO не нужно создавать новый воркбук и принудительно запускать импорт, все само
Помимо Calc у нас в HFLabs популярен libreOffice, особенно под «Линуксом». И то, и другое для CSV применяют активнее, чем Excel.
Белая пустошь, раскинувшаяся посередине, в оригинальном CSV-файле богато заполнена данными
Поэтому после сохранения я еще раз открываю файл и убеждаюсь, что данные на месте.
После пересохранения обязательно еще раз проверяю, что все данные на месте и нет лишних пустых строк.
Если интересно работать с данными, посмотрите на наши вакансии. HFLabs почти всегда нужны аналитики, тестировщики, инженеры по внедрению, разработчики. Данными обеспечим так, что мало не покажется 🙂
dsibi / 1. Принимаемся за статистику
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode characters
Когда данные очищены от мусора, можно приступить к самому интересному — расчётам и презентации результатов заказчику. |
Чему вы научитесь |
Принципам группировки и сортировки данных, расчёту статистики и формированию наглядного отчёта об исследовании. |
Сколько времени это займёт |
1,5 часа = 5 уроков от 1 до 25 минут. |
Постановка задачи |
Завершаем анализ данных Яндекс.Музыки, выполняем поставленную менеджером задачу и сдаём отчёт. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode characters
Идею объединения сервисов Музыка и Радио тестировали на небольшой группе пользователей. Результаты сведены в csv-файл, который вам предстоит изучить. Итог анализа таких данных — это метрики: величины, значения которых отражают пользовательские впечатления. Одна из важнейших — happiness. Здесь это среднее время, которое пользователь слушает музыку в течение выбранного периода времени (в нашей задаче — за сутки). Чем дольше пользователь слушает музыку, тем он довольнее. Ваша задача: найти значение happiness и посмотреть, как оно поменялось с прошлого эксперимента. |
В анализе данных важно наглядное представление результатов, чтобы их мог оценить заказчик. Это бизнес, где на кону громадный трафик, серверные мощности, личное время многотысячной аудитории. И одновременно это исследование. Как в настоящей экспериментальной науке, ответ непредсказуем. Его точность зависит от умелого владения статистическими методами и от качества исходных данных. Перед тем, как начинать считать, проверьте, грамотно ли ваши данные подготовлены. |
Ознакомление с данными: в предыдущих сериях. |
Перед тем, как браться за статистику, нужно: |
1. Прочесть исходный файл и превратить его в структуру данных |
К заданию прилагается файл в формате csv, где все значения разделены запятыми. Это наши исходные данные. Чтобы применить к ним все возможности языка Python и библиотеки Pandas, надо импортировать эту библиотеку и сохранить её в переменной. По сокращённому названию панельных данных (panel data), с которых начиналась Pandas, эту переменную принято называть pd: |
import pandas as pd |
Для чтения csv-файла в библиотеке Pandas есть готовая функция — метод read_csv(). Как и все методы, он вызывается записью через точку после имени своего объекта. В скобках указывается аргумент (параметр) метода. У read_csv() это имя файла с данными. Прочтение превращает файл в структуру данных DataFrame. Имя переменной, в которой эта структура данных сохраняется, чаще всего df либо отражает тематику данных: |
df = pd.read_csv(‘music_log.csv’) |
2. Посмотреть на данные |
Вывести на экран таблицу и оценить данные: |
print(df) |
Как правило, таблица очень велика. Практичнее запросить определённое количество первых строк, методом head(): |
print(df.head(15)) # выведет первые 15 строк таблицы |
3. Оценить качество предподготовки |
Нужно убедиться в том, что данные прошли предподготовку. По крайней мере, не должно быть пропусков и повторов. Пропущенные и неопределённые значения выявляет метод isna(), а суммарное количество таких значений — метод sum(). Обратите внимание: мы записали вызов обоих методов в одну строку, разделив их точкой. Python сначала вызовет метод isna(), а затем результаты его работы передаст методу sum(). |
print(df.isna().sum()) |
Повторяющиеся строки — дубликаты — выявляются методом duplicated() и подсчитываются тем же sum(): |
print(df.duplicated().sum()) |
Если возвращаются нули, всё хорошо — данные пригодны для исследования. |
TASK_1_4 |
Прочитайте данные из файла music_log_upd.csv и выведите первые 10 строк. |
music_log_upd.csv — обновлённый файл с данными, которые прошли предобработку в предыдущей теме. |
SOLUTION |
import pandas as pd |
df = pd.read_csv(‘music_log_upd.csv’) |
print(df.head(10)) |
TASK_2_4 |
Получите список названий столбцов, запросив атрибут columns. Результат выведите на экран. |
SOLUTION |
import pandas as pd |
df = pd.read_csv(‘music_log_upd.csv’) |
print(df.columns) |
TASK_3_4 |
Посчитайте количество пустых значений в наборе данных, сохраните результат в переменной na_number. Выведите её значение на экран. |
SOLUTION |
import pandas as pd |
df = pd.read_csv(‘music_log_upd.csv’) |
na_number=(df.isna().sum()) |
print(na_number) |
TASK_4_4 |
Посчитайте количество дубликатов в наборе данных, сохраните результат в переменной duplicated_number. Выведите её значение на экран. |
SOLUTION |
import pandas as pd |
df = pd.read_csv(‘music_log_upd.csv’) |
duplicated_number=df.duplicated().sum() |
print(duplicated_number) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode characters
Слово «анализ» означает разбор, рассмотрение с разных сторон. Анализ данных начинают с разделения их на группы по какому-нибудь признаку. Эта операция называется группировка данных. Она помогает изучить материал более подробно, чтобы затем перейти к поиску взаимосвязей между отдельными группами. |
Группировка оправданна, если данные чётко делятся по значимому признаку, а полученные группы близки к теме задачи. Например, когда есть данные обо всех покупках в супермаркете, можно смело заниматься группировкой. Так можно установить время наплыва покупателей и решить проблему пиковых нагрузок. Или посчитать средний чек — обычно для магазинов это ключевая метрика. |
Стадии группировки хорошо укладываются в словесную формулу split-apply-combine: |
разделить, split — разбиение на группы по определённому критерию; |
применить, apply — применение какого-либо метода к каждой группе в отдельности, например, подсчёт численности группы методом count() или суммирование вызовом sum(); |
объединить, combine — сведение результатов в новую структуру данных, в зависимости от условий разделения и выполнения метода это бывает DataFrame и Series. |
В библиотеке Pandas есть отличные инструменты группировки. Рассмотрим обращение с ними на примере анализа данных о планетах за пределами Солнечной системы, или экзопланетах. Орбитальные обсерватории засекли уже тысячи таких небесных тел. Их выявляют на снимках космических телескопов наши коллеги, аналитики данных. Поищем среди экзопланет похожие на Землю. Возможно, это наши будущие колонии, или там уже обитают разумные существа, с которыми однажды предстоит установить контакт. |
DataFrame с данными по нескольким тысячам экзопланет сохранён в переменной exoplanet. Посмотрим на первые 30 строк таблицы: |
print(exoplanet.head(30)) |
NAME MASS RADIUS DISCOVERED |
0 1RXS 1609 b 14 19.04 2008 |
1 2M 0122-24 b 20 11.2 2013 |
2 2M 0219-39 b 13.9 16.128 2015 |
3 2M 0746+20 b 12.21 10.864 2010 |
4 2M 2140+16 b 20 10.304 2010 |
5 2M 2206-20 b 30 14.56 2010 |
6 51 Eri b 9.1 12.432 2015 |
7 51 Peg b 0.47 21.28 1995 |
8 55 Cnc e 0.02703 1.94544 2004 |
9 BD+20 594 b 0.0513 2.2288 2016 |
10 BD-10 3166 b 0.46 11.536 2000 |
11 CT Cha b 17 24.64 2008 |
12 CVSO 30 b 6.2 21.392 2012 |
13 CoRoT-1 b 1.03 16.688 2007 |
14 CoRoT-10 b 2.75 10.864 2010 |
15 CoRoT-11 b 2.33 16.016 2010 |
16 CoRoT-12 b 0.917 16.128 2010 |
17 CoRoT-13 b 1.308 9.912 2010 |
18 CoRoT-14 b 7.6 12.208 2010 |
19 CoRoT-15 b 63.4 12.544 2010 |
20 CoRoT-16 b 0.535 13.104 2010 |
21 CoRoT-17 b 2.43 11.424 2010 |
22 CoRoT-18 b 3.47 14.672 2011 |
23 CoRoT-19 b 1.11 14.448 2011 |
24 CoRoT-2 b 3.31 16.408 2007 |
25 CoRoT-20 b 4.24 9.408 2011 |
26 CoRoT-21 b 2.26 14.56 2011 |
27 CoRoT-22 b 0.06 4.87648 2011 |
28 CoRoT-23 b 2.8 12.096 2011 |
29 CoRoT-24 b 0.018 3.696 2011 |
Документация |
Столбцы: |
name: название экзопланеты; |
mass: масса в массах планеты Юпитер; |
radius: радиус, пересчитанный в радиусах Земли; |
discovered: год открытия экзопланеты. |
Источник: каталог экзопланет на портале exoplanet.eu |
На картинке изображен принцип split-apply-combine для таблицы с экзопланетами. Посмотрим, как вообще идут дела с поиском экзопланет. Сначала данные делят по группам, где каждая группа — это год. Потом метод count() подсчитывает численность каждой группы. В итоге получаем новую структуру данных с группами, где каждая содержит год и число открытых за этот год экзопланет. |
image |
В Рandas для группировки данных есть метод groupby(). Он принимает как аргумент название столбца, по которому нужно группировать. В случае с делением экзопланет по годам открытия: |
print(exoplanet.groupby(‘discovered’)) |
Применение метода groupby() к объекту типа DataFrame приводит к созданию объекта особого типа — DataFrameGroupBy. Это сгруппированные данные. Если применить к ним какой-нибудь метод Pandas, они станут новой структурой данных типа DataFrame или Series. |
Подсчитаем сгруппированные по годам экзопланеты методом count(): |
print(exoplanet.groupby(‘discovered’).count()) |
DISCOVERED NAME MASS RADIUS |
1995 1 1 1 |
1996 1 1 1 |
1999 2 2 2 |
2000 5 5 5 |
2001 1 1 1 |
2002 4 4 4 |
2004 10 10 10 |
2005 9 9 9 |
2006 11 11 11 |
2007 23 23 23 |
2008 23 23 23 |
2009 12 12 12 |
2010 59 59 59 |
2011 87 87 87 |
2012 93 93 93 |
2013 98 98 98 |
2014 73 73 73 |
2015 56 56 56 |
2016 84 84 84 |
2017 54 54 54 |
2018 101 101 101 |
2019 2 2 2 |
Результат выполнения кода exoplanet.groupby(‘discovered’).count() — это уже новая структура данных, типа DataFrame. И с первого взгляда на этот DataFrame заметна тенденция: количество открытых экзопланет почти ежегодно растёт. |
Если нужно сравнить наблюдения по одному показателю, метод применяют к DataFrameGroupBy с указанием на один столбец. Нас в первую очередь интересует радиус экзопланет: мы ищем другую Землю. Давайте получим таблицу с единственным столбцом ‘radius’: |
exo_number = exoplanet.groupby(‘discovered’)[‘radius’].count() |
print(exo_number) |
DISCOVERED |
1995 1 |
1996 1 |
1999 2 |
2000 5 |
2001 1 |
2002 4 |
2004 10 |
2005 9 |
2006 11 |
2007 23 |
2008 23 |
2009 12 |
2010 59 |
2011 87 |
2012 93 |
2013 98 |
2014 73 |
2015 56 |
2016 84 |
2017 54 |
2018 101 |
2019 2 |
Name: radius, dtype: int64 |
Получили Series, где по годам открытия расписано количество экзопланет, для которых удалось установить радиус. |
Посмотрим, как меняется средний радиус открытых экзопланет год от года. Для этого надо сложить радиусы планет, открытых за определённый год, и поделить на их количество (которое мы уже нашли). |
Сумма радиусов считается методом sum(): |
exo_radius_sum = exoplanet.groupby(‘discovered’)[‘radius’].sum() |
print(exo_radius_sum) |
DISCOVERED |
1995 21.280000 |
1996 11.872000 |
1999 26.992000 |
2000 57.198400 |
2001 10.315200 |
2002 47.152000 |
2004 110.988640 |
2005 111.059200 |
2006 246.568000 |
2007 325.908800 |
2008 350.884800 |
2009 130.959289 |
2010 723.900182 |
2011 917.345484 |
2012 707.924857 |
2013 705.458700 |
2014 554.762932 |
2015 563.962784 |
2016 971.348000 |
2017 504.473312 |
2018 994.195820 |
2019 14.324800 |
Name: radius, dtype: float64 |
Очень кстати, что объекты Series можно делить друг на друга. Это позволит нам разделить перечень сумм радиусов на перечень количеств экзопланет без перебора в цикле: |
exo_radius_mean = exo_radius_sum/exo_number |
print(exo_radius_mean) |
DISCOVERED |
1995 21.280000 |
1996 11.872000 |
1999 13.496000 |
2000 11.439680 |
2001 10.315200 |
2002 11.788000 |
2004 11.098864 |
2005 12.339911 |
2006 22.415273 |
2007 14.169948 |
2008 15.255861 |
2009 10.913274 |
2010 12.269495 |
2011 10.544201 |
2012 7.612095 |
2013 7.198558 |
2014 7.599492 |
2015 10.070764 |
2016 11.563667 |
2017 9.342098 |
2018 9.843523 |
2019 7.162400 |
Name: radius, dtype: float64 |
Точность наших приборов растёт, и новые экзопланеты по размерам всё ближе к Земле. За 24 года средний радиус обнаруженных планет снизился втрое. |
Тем же методом groupby(), которым мы ищем новую Землю, можно поискать и необыкновенного человека в данных Яндекс.Музыки. Тем более, что без этого не выполнить поставленной менеджером задачи. |
Прежде, чем рассчитывать метрику happiness, нужно изучить пользователей, чьё «счастье» мы собираемся оценить. Какие они, эти люди, которые слушают действительно много музыки? Есть ли у них особые предпочтения, или они потребляют всё подряд? |
TASK_1_3 |
Меломаны у нас есть. Сейчас узнаем идентификатор user_id одного из них. Для этого сгруппируем данные по каждому пользователю, чтобы собрать жанры прослушанных им композиций. |
Сгруппируйте DataFrame по столбцу user_id, сохраните полученный результат в переменной genre_grouping. |
Посчитайте количество жанров, которые выбрали пользователи, методом count(), указав, что выбираем один столбец genre_name. Сохраните результат в переменной genre_counting и выведите первые 30 строк этой таблицы. |
SOLUTION |
import pandas as pd |
df = pd.read_csv(‘music_log_upd.csv’) |
genre_grouping=df.groupby(«user_id») |
genre_counting=genre_grouping[‘genre_name’].count() |
print(genre_counting.head(30)) |
TASK_2_3 |
Быть может, те, кто за день слушает больше 50 песен, имеют более широкие предпочтения. Чтобы найти такого, изготовим универсальный инструмент. |
Напишите функцию user_genres, которая принимает некую группировку как свой аргумент group. Функция должна перебирать группы, входящие в эту группировку. |
В каждой группе два элемента — имя группы с индексом 0 и список значений с индексом 1. |
Обнаружив такую группу, в которой список (элемент с индексом 1) содержит более 50 значений, функция возвращает имя группы (значение элемента с индексом 0). |
SOLUTION |
import pandas as pd |
df = pd.read_csv(‘music_log_upd.csv’) |
def user_genres(group): |
for col in group: |
if len(col[1]) > 50:# назначьте условие: если длина столбца col с индексом 1 больше 50, тогда |
user = col[0]# в переменной user сохраняется элемент col[0] |
return user |
TASK_3_3 |
Вызовите функцию user_genres, как аргумент передайте ей genre_grouping. Результат – user_id неведомого нам любителя музыки – сохраните в переменной search_id и выведите значение на экран. |
SOLUTION |
import pandas as pd |
df = pd.read_csv(‘music_log_upd.csv’) |
genre_grouping = df.groupby(‘user_id’)[‘genre_name’] |
#print(genre_grouping.head(30)) |
def user_genres(group): |
for col in group: |
if len(col[1]) > 50: |
user = col[0] |
return user |
search_id=user_genres(genre_grouping) |
print(search_id) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode characters
Поиск необычного в группе — что среди планет, что среди меломанов — это прежде всего поиск чемпионов: объектов с выдающимися показателями по разным статьям. Как всю таблицу, так и отдельные группы изучают, сортируя строки по какому-либо столбцу. В Pandas для этой операции есть метод sort_values(). У него два аргумента: |
• by = ‘имя столбца’ — имя столбца, по которому нужно сортировать; |
• ascending: по умолчанию True. Для сортировки по убыванию установите значение False. |
image |
Этот аргумент уже знаком вам по аналогичному методу sort() в языке Python. |
Среди экзопланет интересны близкие по размерам к Земле. Есть ли такие? Отсортируем список по радиусу в порядке возрастания. Тогда в голове таблицы окажутся самые малые, на которых гравитация не прижмёт нас к полу. |
print(exoplanet.sort_values(by = ‘radius’).head(30)) |
NAME MASS RADIUS DISCOVERED |
469 Kepler-37 b 0.00875 0.3192 2013 |
359 Kepler-102 b 0.0013 0.4704 2014 |
541 Kepler-62 c 0.0126 0.5376 2013 |
490 Kepler-42 d 0.003 0.5712 2012 |
390 Kepler-138 b 0.00021 0.5824 2014 |
360 Kepler-102 c 0.009 0.5824 2013 |
489 Kepler-42 c 0.006 0.728 2012 |
470 Kepler-37 c 0.031463 0.749399 2013 |
552 Kepler-70 b 0.014 0.7616 2011 |
625 TRAPPIST-1 d 0.0013 0.77168 2016 |
488 Kepler-42 b 0.009 0.784 2012 |
481 Kepler-408 b 0.02 0.8176 2013 |
367 Kepler-106 b 0.00047 0.8176 2014 |
389 Kepler-131 c 0.026 0.84 2014 |
346 KIC 12557548 b 6.3e-05 0.849319 2012 |
479 Kepler-406 c 0.00853 0.8512 2014 |
417 Kepler-20 e 0.0097 0.86464 2011 |
553 Kepler-70 c 0.0021 0.8736 2011 |
363 Kepler-102 f 0.002 0.8848 2013 |
288 K2-137 b 0.7 0.8848 2017 |
626 TRAPPIST-1 e 0.002 0.91728 2017 |
550 Kepler-68 c 0.00686 0.92624 2013 |
369 Kepler-106 d 0.025 0.952 2014 |
64 EPIC 248545986 c 0.0028 0.9968 2018 |
418 Kepler-20 f 0.045 1.00218 2011 |
627 TRAPPIST-1 f 0.0021 1.04418 2017 |
624 TRAPPIST-1 c 0.00434 1.05515 2016 |
350 KOI-2700 b 0.00271 1.05952 2013 |
623 TRAPPIST-1 b 0.0027 1.08517 2016 |
534 Kepler-59 b 2.05 1.0976 2012 |
Оказывается, некоторые из уже открытых экзопланет по размерам близки не то что к Земле, но уже и к Луне! Получим список экзопланет с радиусом меньше земного. Смотрите, как логический оператор (здесь это 50: |
user = col[0] |
return user |
search_id = user_genres(genre_grouping) |
music_user=df[(df[‘user_id’]==search_id)&(df[«total_play_seconds»]!=0)] |
print(music_user) |
TASK_2_5 |
Теперь узнаем, сколько времени он слушал музыку каждого жанра. |
Сгруппируйте данные таблицы music_user по столбцу ‘genre_name’ и получите сумму значений столбца ‘total_play_seconds’. Сохраните результат в переменной sum_music_user и выведите её значение на экран. |
SOLUTION |
import pandas as pd |
df = pd.read_csv(‘music_log_upd.csv’) |
genre_grouping = df.groupby(‘user_id’)[‘genre_name’] |
def user_genres(group): |
for col in group: |
if len(col[1]) > 50: |
user = col[0] |
return user |
search_id = user_genres(genre_grouping) |
music_user=df[(df[‘user_id’]==search_id)&(df[«total_play_seconds»]!=0)] |
#print(music_user) |
sum_music_user=music_user.groupby(«genre_name»)[‘total_play_seconds’].sum() |
print(sum_music_user) |
TASK_3_5 |
Кажется, предпочтения нашего меломана начинают проявляться. Но, возможно, длительность композиций от жанра к жанру сильно различается. Важно знать, сколько треков каждого жанра он включил. |
Сгруппируйте данные по столбцу genre_name и посчитайте, сколько значений в столбце genre_name. Сохраните результат в переменной count_music_user и выведите её значение на экран. |
Чтобы команда «распечатать сумму» из прошлой задачи не мешала рассматривать новое решение, закомментируйте её. |
SOLUTION |
import pandas as pd |
df = pd.read_csv(‘music_log_upd.csv’) |
genre_grouping = df.groupby(‘user_id’)[‘genre_name’] |
def user_genres(group): |
for col in group: |
if len(col[1]) > 50: |
user = col[0] |
return user |
search_id = user_genres(genre_grouping) |
music_user=df[(df[‘user_id’]==search_id)&(df[«total_play_seconds»]!=0)] |
#print(music_user) |
sum_music_user=music_user.groupby(«genre_name»)[‘total_play_seconds’].sum() |
#print(sum_music_user) |
count_music_user=music_user.groupby(«genre_name»)[‘genre_name’].count() |
print(count_music_user) |
TASK_4_5 |
Чтобы предпочтения были видны сразу, нужно крупнейшие значения расположить наверху. Отсортируйте данные в группировке sum_music_user по убыванию. Внимание: когда применяете метод sort_values() к Series с единственным столбцом, аргумент by указывать не нужно, только порядок сортировки. |
Сохраните результат в переменной final_sum и выведите её значение на экран. |
Команду «распечатать сумму» из прошлой задачи закомментируйте. |
SOLUTION |
import pandas as pd |
df = pd.read_csv(‘music_log_upd.csv’) |
genre_grouping = df.groupby(‘user_id’)[‘genre_name’] |
def user_genres(group): |
for col in group: |
if len(col[1]) > 50: |
user = col[0] |
return user |
search_id = user_genres(genre_grouping) |
music_user=df[(df[‘user_id’]==search_id)&(df[«total_play_seconds»]!=0)] |
#print(music_user) |
sum_music_user=music_user.groupby(«genre_name»)[‘total_play_seconds’].sum() |
#print(sum_music_user) |
count_music_user=music_user.groupby(«genre_name»)[‘genre_name’].count() |
#print(count_music_user) |
final_sum=sum_music_user.sort_values(ascending=False) |
print(final_sum) |
TASK_5_5 |
Теперь то же самое надо сделать с числом прослушанных меломаном композиций. Отсортируйте данные группировки count_music_user по убыванию. Сохраните результат в переменной final_count, значение которой выведите на экран. |
Команду «распечатать» из прошлой задачи закомментируйте. |
SOLUTION |
import pandas as pd |
df = pd.read_csv(‘music_log_upd.csv’) |
genre_grouping = df.groupby(‘user_id’)[‘genre_name’] |
def user_genres(group): |
for col in group: |
if len(col[1]) > 50: |
user = col[0] |
return user |
search_id = user_genres(genre_grouping) |
music_user=df[(df[‘user_id’]==search_id)&(df[«total_play_seconds»]!=0)] |
#print(music_user) |
sum_music_user=music_user.groupby(«genre_name»)[‘total_play_seconds’].sum() |
#print(sum_music_user) |
count_music_user=music_user.groupby(«genre_name»)[‘genre_name’].count() |
#print(count_music_user) |
final_sum=sum_music_user.sort_values(ascending=False) |
#print(final_sum) |
final_count=count_music_user.sort_values(ascending=False) |
print(final_count) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode characters