Руководство по проекту для начинающих в области науки о данных с использованием данных League of Legends

Введение

В первый раз, когда я построил гистограмму, я не мог сразу понять , как добавлять метки к столбцам. Особенно беспокоили меня горизонтальные гистограммы. Так что не беспокойтесь, если у вас тоже не получается заставить его работать . В этом посте вы узнаете, как это делается, а также несколько советов и приемов. для стилизации диаграммы.

ПРИМЕЧАНИЕ. Если вам нужно только увидеть, как добавлять метки, вы можете пропустить раздел: Создание гистограмм с метками.
Я включил весь код, который я написал для вас, чтобы вы могли его скопировать, если вы хотите печатать во время чтения, а также подробные объяснения того, как работает код.

Чтобы научить вас создавать гистограммы с метками, мне потребовались некоторые данные для работы. Поэтому я решил взять некоторые игровые данные из онлайн-игры League of Legends . Это делает руководство более интересным для поклонников игры, (включая меня) . Первая часть сообщения - это прогулка по тому, как я очищаю данные, чтобы привести их в предпочтительный для меня формат. Я рекомендую вам прочитать эту часть, а также она учит вас конвертировать данные из формата JSON в Pandas DataFrame.

Пост имеет следующую структуру:

Подготовка данных (JSON в Pandas DataFrame)

У меня есть файл JSON, содержащий базовую статистику (характеристики) всех чемпионов (игровых персонажей) в League of Legends. Вы можете получить доступ к файлу данных здесь. Данные выглядят так (см. снимок экрана):

Всего есть 153 чемпиона, которые все включают одни и те же ключи в свои словари.

В конечном итоге мы создадим гистограмму, которая покажет начальные hp (очки жизни) чемпионов, чтобы получить представление о том, какие чемпионы в начале имеют наибольшее здоровье. Если вам интересна другая статистика, вам будет очень легко изменить то, что показывает гистограмма, например, вам может быть интересно узнать, у каких чемпионов больше всего рекламы (урон от атаки).

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

Я призываю вас попробовать кодировать, так как это даст вам лучшее понимание! Учиться на собственном опыте.

import pandas as pd
import matplotlib.pyplot as plt
import json

Я начинаю с импорта необходимых библиотек, которые мы будем использовать. Pandas позволяет нам создавать DataFrames, что является хорошей структурой для работы, когда мы занимаемся наукой о данных. Matplotlib.pyplot будет использоваться для построения диаграмм. json позволит вам загрузить данные из файла JSON в память, чтобы мы могли с ними работать.

with open('champ_stats.json') as f:
  data = json.load(f)
champion = data['Aatrox']
stat_names = list(champion.keys())
columns = ['champ'] + stat_names
df = pd.DataFrame()

Я открываю файл данных и загружаю его в переменную data. Затем я хотел бы иметь список, содержащий названия всех различных характеристик (статистика / характеристики чемпиона), так как это будут имена столбцов для готового DataFrame. Список статистики включен во все словари чемпионов в данных, поэтому нам просто нужно погрузиться в один из них и взять ключи из этого словаря. Мы получаем доступ к клавише 'Aatrox', поскольку это первый защитник данных. Это дает нам соответствующее значение, которое мы сохраняем в переменной champion. Это значение - еще один словарь, в котором все ключи - это имена, которые мы ищем, поэтому мы используем функцию .keys() для получения ключей, а возвращаемое значение преобразуется в список, поскольку функция была вызвана внутри функции list(). Если этот шаг вас смущает, я написал другое руководство, в котором объясняется, как получить доступ к данным в словарях Python, которые вы можете найти здесь.

Имена столбцов для DataFrame будут 'champ' для первого столбца, который будет содержать все имена чемпионов, а другие столбцы будут названы в соответствии с элементами в stat_names. Мы сохраняем это в переменной columns. Наконец, мы используем pd.DataFrame(), чтобы создать пустой DataFrame, который мы сохраняем в переменной df.

for name, stats in data.items():
  values = [stats[x] for x in columns[1:]]
  row = pd.DataFrame([[name] + values], columns=columns)
  df = df.append(row)
df.head()

На предыдущем шаге у нас был пустой DataFrame, и теперь пора заполнить его строками данных, где каждая строка представляет чемпиона. Теперь мы создаем оператор for, который перебирает все пары ключ-значение в словаре данных, (name - ключ, а stats - значение ). Нам нужен список всех значений статистики, и для его получения я использую понимание списка и сохраняю полученный список в переменной values. Понимание списка работает следующим образом: для каждого имени (x) в columns[1:] мы берем это имя и используем его в качестве ключа для доступа к соответствующему значению из stats, и это значение добавляется в список.

Затем мы создаем новый DataFrame, который сохраняем в переменной row . Этот DataFrame создается с двумя параметрами. Во-первых, список внутри списка, где внутренний список содержит имя и все значения для текущего чемпиона. Во-вторых, формальный атрибут columns= установлен в нашу переменную списка, также называемую columns, это установит имена столбцов в DataFrame так, как мы хотим.
Затем мы добавляем row к основному DataFrame, который мы назвали df, это возвращает новый DataFrame, с помощью которого мы переопределяем предыдущий df.

Наконец, мы вызываем метод head() на df для отображения первых строк DataFrame. Если вы написали код, вы должны получить следующий результат:

Данные теперь красиво отформатированы как DataFrame, и на следующем шаге мы, наконец, создадим гистограммы и добавим метки.

Создание гистограмм с метками

df_sorted_by_hp = df.sort_values('hp', ascending=False)
x = df_sorted_by_hp['champ'][:15]
y = df_sorted_by_hp['hp'][:15]

Чтобы улучшить диаграмму, я решил отсортировать строки в DataFrame по значению 'hp', а ascending=False сортирует значения в порядке убывания. После этого мы сохраняем столбец чемпионов в переменную с именем x и аналогично значения hp в переменную y. Чтобы диаграмма не была слишком загромождена, я решил включить только первых 15 чемпионов, это сделано с суффиксом [:15].

fig, ax = plt.subplots(figsize=(20,4))
bars = ax.bar(x, y, width=0.5)

Мы используем plt.subplots(figsize=(20,4)) для создания объекта фигуры и объекта оси, которые сохраняются в переменных fig и ax. Мы установили размер фигуры 20 на 4 дюйма, это может быть не самый лучший размер, но вы можете поиграть с числами, пока не выясните, какой размер лучше всего подходит для вашей диаграммы. ax.bar(x, y, width=0.5) создает гистограмму со значениями x и y и width равным 0,5, опять же, вы можете попробовать разные размеры от 0 до 1 для ширины. Я сохраняю возвращенный объект в переменной bars, которую мы вскоре будем использовать.

Код создаст следующую диаграмму, (только ее часть, чтобы она лучше подходила на Medium):

… Не очень круто

Во-первых, очень сложно различить разницу в высоте столбцов, а во-вторых, невозможно определить точную высоту каждой планки. Вот тут и пригодятся ярлыки!
Давайте добавим их сейчас!

Примечание. Добавьте следующий фрагмент кода к предыдущему.

for bar in bars:
  height = bar.get_height()
  label_x_pos = bar.get_x() + bar.get_width() / 2
  ax.text(label_x_pos, height, s=f'{height}', ha='center',
  va='bottom')

Мы можем перебрать переменную bars, чтобы пройти по каждому столбцу на графике. Мы сохраняем высоту каждой полосы в переменной с именем height, получая ее из функции bar.get_height(). Затем нам нужна позиция x для метки текущего бара в цикле. Мы получаем эту позицию из функции bar.get_x() и складываем ширину полосы, разделенную на 2, чтобы получить значение x для центра полосы.

Наконец, мы используем ax.text(label_x_pos, height, s=f'{height}', ha='center') для создания метки / текста. Эта функция принимает значение для позиции x и одно для позиции y, затем мы даем ей строку, которую она будет отображать, которая в данном случае является высотой полосы. Наконец, ha='center' дополнительно помогает выровнять метку по центру полосы, а va='bottom' помещает метку прямо над полосой.

В результате будет получена следующая диаграмма, (только ее часть, чтобы она лучше подходила для Medium):

Да! Теперь у нас есть метки для полос, отображающие их индивидуальную высоту!
Это явно лучше, но все еще можно улучшить.

Я думаю, что имена под полосками немного трудно читать из-за их размера. Также я хотел бы заголовок для диаграммы вместе с описанием осей. Кроме того, нет смысла начинать ось y с нуля. Наконец, я думаю, диаграмму можно было бы приправить цветом!

Примечание. Добавьте следующий фрагмент кода к предыдущему.

for tick in ax.xaxis.get_major_ticks():
  tick.label.set_fontsize(14)
for bar in bars[::2]:
  bar.set_color('r')
plt.title('The 15 champions with the most HP')
plt.xlabel('Champion name')
plt.ylabel('Starting hp')
plt.ylim([550,650])
plt.show()

Вы можете установить размер шрифта для каждого тика, перебирая тики, возвращаемые из ax.xaxis.get_major_ticks(), и предоставляя размер шрифта с помощью tick.label.set_fontsize(14). Точно так же вы можете получить доступ к каждой второй полосе с помощью for bar in bars[::2]: и установить цвет с помощью bar.set_color('r'). Заголовок, метка x и метка y могут быть установлены с соответствующими функциями. Наконец, вы можете ограничить ось y (то же самое и для оси x) с помощью plt.ylim([550, 650]). Я считаю, что начиная с 550 и заканчивая 650 работала эта диаграмма.

plt.show() покажет готовую линейчатую диаграмму, (только ее часть, чтобы она лучше соответствовала среднему уровню):

Думаю, это большое улучшение!

Чтобы создать горизонтальную гистограмму, нам нужно очень немного изменить. По этой причине я покажу код в одном фрагменте и выделю изменения полужирным шрифтом. Также имейте в виду, что данные те же, что и раньше, и поэтому у нас все еще есть все имена в переменной x и все значения hp в переменной y.

fig, ax = plt.subplots(figsize=(4, 10))
bars = ax.barh(x,y, 0.5)
for bar in bars:
  width = bar.get_width() #Previously we got the height
  label_y_pos = bar.get_y() + bar.get_height() / 2
  ax.text(width, label_y_pos, s=f'{width}', va='center')
for tick in ax.yaxis.get_major_ticks():
  tick.label.set_fontsize(14)
for bar in bars[::2]:
  bar.set_color('r')
plt.title('The 15 champions with the most HP')
plt.xlabel('Starting hp')
plt.ylabel('Champion name')
plt.xlim([550, 650])
plt.show()

Как и раньше, мы создаем объекты фигуры и осей и устанавливаем размер, однако на этот раз размер (4, 10) работает лучше (выше, чем в ширину). Чтобы создать горизонтальную гистограмму, мы используем ax.barh() вместо ax.bar(). Затем вместо высоты каждой полосы мы получаем ширину. Нам нужно получить позицию y полосок, используя bar.get_y() вместо позиции x, и мы добавляем высоту полосы, деленную на 2, (обратите внимание, что высота здесь имеет другое значение по сравнению с предыдущим) .

Мы снова устанавливаем метку для панели с ax.text(), однако мы используем width для позиции x и для отображаемой строки. Кроме того, мы используем аргумент va='center', это помогает разместить метку в центре полосы. Попробуйте удалить его, чтобы увидеть разницу.

Для тактов мы просто изменяем размер y тактов вместо x. Наконец, вам нужно поменять метки для осей, а также ограничить ось x по отношению к оси y.
Результат:

Вуаля, теперь вы знаете, как создавать как вертикальные, так и горизонтальные гистограммы, а также добавлять метки, чтобы сделать их более читаемыми.

Резюме

Если вы следовали всему руководству, то теперь вы создали небольшой проект, в котором вы преобразовали данные JSON в Pandas DataFrame, а затем создали гистограммы с метками.

Лично я предпочитаю горизонтальную гистограмму, поскольку мне легче читать в этом случае.
Помните, что вы можете продолжить изучение данных, если вам интересно. Попробуйте выяснить, у какого чемпиона меньше всего здоровья, или покажите больше чемпионов в своих таблицах.

Если у вас есть какие-либо вопросы, комментарии или советы по улучшению публикации, не стесняйтесь обращаться ко мне!

Спасибо, что нашли время прочитать этот пост, надеюсь, вы узнали что-то полезное!

Продолжайте учиться!
- Якоб Тофтгаард Расмуссен