В рамках этого семинара мы поработаем с файлом в формате JSON.
JSON расшифровывается как JavaScript Object Notation. Изначально этот формат хранения данных использовался в языке JavaScript, но теперь он потерял привязку к конкретному языку программирования и стал универсальным. С форматом JSON можно столкнуться при обращении к API, базам данных; формат часто применяется для хранения информации на сервере, к которому обращается сайт, например, в зависимости от запросов пользователей. Object здесь можно понимать как некоторую структуру хранения данных (список, кортеж, словарь), которая записывается в специальном виде, внешне напоминающим обычную строку.
Импортируем модуль json
:
import json
Начнём работать с реальными данными. Зайдём на Портал открытых данных Правительства Москвы в раздел Образование и выберем набор данных Победители олимпиад. Кликнем Экспорт и скачаем файл в формате json
. Скачается, правда, zip-архив, но его можно распаковать. Сохраним название файла (или путь к нему) в переменную name
:
name = "data-27257-2020-03-02.json"
Теперь загрузим содержимое файла в Python. Вообще при работе с json-файлами выделяют две операции: десериализация и сериализация. Десериализация – это преобразование объекта JSON в другую структуру данных (например, в питоновский словарь или список), а сериализация – это запись данных в формат JSON. Десериализуем:
with open(name, "r", encoding="Windows-1251") as read_file:
data = json.load(read_file)
Запись с with open()
as read_file
эквивалентна созданию переменной read_file
и присваиванию ей значения из open()
. Плюс, так как текст в файле на кириллице, при загрузке файла имеет смысл указать кодировку (здесь это Windows-1251
), иначе файл может не открыться или открыться, но с крокозябрами вместо букв.
Посмотрим на data
:
data
В переменной data
сохранён список словарей. Можем посмотреть на первый элемент списка:
data[0]
{'global_id': 4472939, 'Year': '2012/2013', 'ShortName': 'ГБОУ лицей «Вторая школа»', 'OlympiadType': 'Всероссийская олимпиада', 'Stage': '3', 'Class': '11', 'Subject': 'Иностранный язык (английский язык)', 'Status': 'призёр', 'FullName': 'Государственное бюджетное общеобразовательное учреждение города Москвы «Лицей «Вторая школа»'}
Если привести данные в таком формате к привычному табличному виду, то получится, что в таблице у нас есть 9 столбцов (с global_id
по FullName
), а каждая строка таблицы описывается словарём как в ячейке выше. Данные в таком формате удобно хранить, к ним удобно писать запросы, выбирая значения по ключам в словарях, но иногда логичнее поместить их в таблицу. Для этого нам понадобится библиотека pandas
:
import pandas as pd
Превратим список словарей data
в таблицу (датафрейм pandas
):
olymp = pd.DataFrame(data)
Посмотрим на первые несколько строк:
olymp.head()
global_id | Year | ShortName | OlympiadType | Stage | Class | Subject | Status | FullName | |
---|---|---|---|---|---|---|---|---|---|
0 | 4472939 | 2012/2013 | ГБОУ лицей «Вторая школа» | Всероссийская олимпиада | 3 | 11 | Иностранный язык (английский язык) | призёр | Государственное бюджетное общеобразовательное ... |
1 | 4472940 | 2012/2013 | ГБОУ лицей «Вторая школа» | Всероссийская олимпиада | 3 | 11 | Иностранный язык (английский язык) | призёр | Государственное бюджетное общеобразовательное ... |
2 | 4472941 | 2012/2013 | ГБОУ лицей «Вторая школа» | Всероссийская олимпиада | 3 | 10 | Иностранный язык (английский язык) | призёр | Государственное бюджетное общеобразовательное ... |
3 | 4472942 | 2012/2013 | ГБОУ СОШ № 26 | Всероссийская олимпиада | 4 | 11 | Иностранный язык (английский язык) | победитель | Государственное бюджетное образовательное учре... |
4 | 4472943 | 2012/2013 | ГБОУ СОШ № 26 | Всероссийская олимпиада | 3 | 11 | Иностранный язык (английский язык) | призёр | Государственное бюджетное образовательное учре... |
Все задания выполняются на основе датафрейма olymp
, полученного в предыдущей части.
Приведите столбец Class
к целочисленному типу.
olymp["Class"] = olymp["Class"].astype(int)
olymp.info() # проверим
<class 'pandas.core.frame.DataFrame'> RangeIndex: 66122 entries, 0 to 66121 Data columns (total 9 columns): global_id 66122 non-null int64 Year 66122 non-null object ShortName 66122 non-null object OlympiadType 66122 non-null object Stage 41702 non-null object Class 66122 non-null int64 Subject 66122 non-null object Status 66122 non-null object FullName 66122 non-null object dtypes: int64(2), object(7) memory usage: 4.5+ MB
Выведите уникальные значения в столбце OlympiadType
.
olymp["OlympiadType"].unique()
array(['Всероссийская олимпиада', 'Московская олимпиада'], dtype=object)
Сгруппируйте строки в соответствии со значениями в столбце OlympiadType
и сохраните в отдельные csv-файл данные по Всероссийской олимпиаде и по Московской олимпиаде. Файлы должны называться Всероссийская олимпиада.csv
и Московская олимпиада.csv
.
for name, dat in olymp.groupby("OlympiadType"):
dat.to_csv(name + ".csv")
Выведите таблицу с числом участников олимпиад с группировкой по типу олимпиады и статусу участника. Сохраните полученный результат в переменную res
.
res = olymp.groupby(["OlympiadType", "Status"]).agg('count')
res
global_id | Year | ShortName | Stage | Class | Subject | FullName | ||
---|---|---|---|---|---|---|---|---|
OlympiadType | Status | |||||||
Всероссийская олимпиада | победитель | 4581 | 4581 | 4581 | 4581 | 4581 | 4581 | 4581 |
призёр | 37121 | 37121 | 37121 | 37121 | 37121 | 37121 | 37121 | |
Московская олимпиада | победитель | 4840 | 4840 | 4840 | 0 | 4840 | 4840 | 4840 |
призёр | 19580 | 19580 | 19580 | 0 | 19580 | 19580 | 19580 |
a) Используя результат в переменной res
, выведите на экран число победителей и призеров только Всероссийской олимпиады.
b) Используя результат в переменной res
, выведите на экран число победителей и призеров только Московской олимпиады.
c) Используя результат в переменной res
, выведите на экран число победителей Всероссийской олимпиады. Это должно быть одно число.
# a
res.xs("Всероссийская олимпиада", level = "OlympiadType")
global_id | Year | ShortName | Stage | Class | Subject | FullName | |
---|---|---|---|---|---|---|---|
Status | |||||||
победитель | 4581 | 4581 | 4581 | 4581 | 4581 | 4581 | 4581 |
призёр | 37121 | 37121 | 37121 | 37121 | 37121 | 37121 | 37121 |
# b
res.xs("Московская олимпиада", level = "OlympiadType")
global_id | Year | ShortName | Stage | Class | Subject | FullName | |
---|---|---|---|---|---|---|---|
Status | |||||||
победитель | 4840 | 4840 | 4840 | 0 | 4840 | 4840 | 4840 |
призёр | 19580 | 19580 | 19580 | 0 | 19580 | 19580 | 19580 |
# c
res.iloc[0, 1]
4581
Выведите медианное значение класса для каждого типа олимпиады и статуса участника, используя группировку и метод для агрегирования. Сохраните результат в переменную res2
. Удалите в res2
столбец global_id
, переименуйте столбец Class
в Класс (медиана)
.
res2 = olymp.groupby(["OlympiadType",
"Status"]).agg('median')
res2
global_id | Class | ||
---|---|---|---|
OlympiadType | Status | ||
Всероссийская олимпиада | победитель | 425397348.0 | 10.0 |
призёр | 425105693.0 | 10.0 | |
Московская олимпиада | победитель | 425405397.5 | 8.0 |
призёр | 425404716.5 | 8.0 |
# удаляем столбец
res2.drop(columns=["global_id"], inplace = True)
res2
Class | ||
---|---|---|
OlympiadType | Status | |
Всероссийская олимпиада | победитель | 10.0 |
призёр | 10.0 | |
Московская олимпиада | победитель | 8.0 |
призёр | 8.0 |
# переименовываем
res2.rename(columns = {'Class':'Класс (медиана)'},
inplace = True)
res2
Класс (медиана) | ||
---|---|---|
OlympiadType | Status | |
Всероссийская олимпиада | победитель | 10.0 |
призёр | 10.0 | |
Московская олимпиада | победитель | 8.0 |
призёр | 8.0 |
res2.to_html()
'<table border="1" class="dataframe">\n <thead>\n <tr style="text-align: right;">\n <th></th>\n <th></th>\n <th>Класс (медиана)</th>\n </tr>\n <tr>\n <th>OlympiadType</th>\n <th>Status</th>\n <th></th>\n </tr>\n </thead>\n <tbody>\n <tr>\n <th rowspan="2" valign="top">Всероссийская олимпиада</th>\n <th>победитель</th>\n <td>10.0</td>\n </tr>\n <tr>\n <th>призёр</th>\n <td>10.0</td>\n </tr>\n <tr>\n <th rowspan="2" valign="top">Московская олимпиада</th>\n <th>победитель</th>\n <td>8.0</td>\n </tr>\n <tr>\n <th>призёр</th>\n <td>8.0</td>\n </tr>\n </tbody>\n</table>'