#python #pandas #list #matplotlib #seaborn
Вопрос:
Допустим, у меня есть фрейм данных, структурированный следующим образом:
Name x y
Joe 0,1,5 0,3,8
Sue 0,2,8 1,9,5
...
Harold 0,5,6 0,7,2
Я хотел бы отобразить значения по осям x и y на линейном графике на основе строки. На самом деле существует много значений x и y, но для каждого значения y в этих столбцах всегда есть одно значение x. Название участка будет значением в поле «имя».
Я попытался сделать это, сначала преобразовав x и y в списки в их собственных отдельных столбцах, например:
df['xval'] = df.['x'].str.split(',')
df['yval'] = df.['y'].str.split(',')
А затем передал их сиборну:
ax = sns.lineplot(x=df['xval'], y=df['yval'], data=df)
Однако это не работает, потому что 1) Я получаю ошибку, которая, как я предполагаю, связана с попыткой передать список из фрейма данных, утверждая, что:
TypeError: unhashable type: 'list'
И 2) Я не могу указать значение df[‘имя’] для конкретного линейного графика. Каков наилучший способ решения этой проблемы?
Комментарии:
1. Что вы ожидаете
sns.lineplot
получить, учитывая, что каждое значениеxval
иyval
является списком? Каков ваш желаемый результат?2. @larsks Мой желаемый результат — иметь линейную диаграмму для каждой строки, где имя графика является значением для столбца name, ось x-это значения из списка столбца x, а ось y-значения из списка столбца y.
Ответ №1:
Данные и импорт:
import pandas as pd
import seaborn as sns
from matplotlib import pyplot as plt
df = pd.DataFrame({
'name': ['joe', 'sue', 'mike'],
'x': ['0,1,5', '0,2,8', '0,4'],
'y': ['0,3,8', '1,9,5', '1,6']
})
Мы должны преобразовать df
в удобный формат для построения графиков. Это делает все построения более сложными. Мы можем воспользоваться этим фактом x
и y
установить отношения 1 к 1. Обратите внимание, что я добавил третье имя со значением 2 xy вместо 3, чтобы показать, что этот метод будет работать для разных значений x и y на имя, если в каждой строке одинаковое количество значений x и y.
Создание plot_df
:
# Grab Name Column to Start Plot DF with
plot_df = df.loc[:, ['name']]
# Split X column
plot_df['x'] = df['x'].str.split(',')
# Explode X into Rows
plot_df = plot_df.explode('x').reset_index(drop=True)
# Split and Series Explode y in one step
# This works IF AND ONLY IF a 1-to-1 relationship for x and y
plot_df['y'] = df['y'].str.split(',').explode().reset_index(drop=True)
# These need to be numeric to plot correctly
plot_df.loc[:, ['x', 'y']] = plot_df.loc[:, ['x', 'y']].astype(int)
plot_df
:
name x y
0 joe 0 0
1 joe 1 3
2 joe 5 8
3 sue 0 1
4 sue 2 9
5 sue 8 5
6 mike 0 1
7 mike 4 6
Ссылки на методы, используемые при создании plot_df
:
DataFrame.loc
для подмножества фрейма данныхSeries.str.split
чтобы разделить значения, разделенные запятыми, на списокDataFrame.explode
для масштабирования фрейма данных на основе итеративного вx
DataFrame.reset_index
чтобы снова сделать индекс уникальным после взрываSeries.explode
чтобы увеличить масштаб списков в серииy
.Series.reset_index
чтобы снова сделать индекс уникальным после взрываDataFrame.astype
поскольку значения изначально являются строками, простого разделения и взрыва недостаточно. Нужно будет преобразовать их в числовой тип, чтобы они могли правильно строить
Построение графика (Вариант 1)
# Plot with hue set to name.
sns.lineplot(data=plot_df, x='x', y='y', hue='name')
plt.show()
Ссылки для построения отдельных строк:
sns.lineplot
к заговору. Обратите внимание наhue
аргумент для создания отдельных строк на основеname
.pyplot.show
для отображения.
Построение (Вариант 2.а) Подзаголовков:
sns.relplot(data=plot_df, x='x', y='y', col='name', kind='line')
plt.tight_layout()
plt.show()
Построение (Вариант 2.b) Подзаголовков:
# Use Grouper From plot_df
grouper = plot_df.groupby('name')
# Create Subplots based on the number of groups (ngroups)
fig, axes = plt.subplots(nrows=grouper.ngroups)
# Iterate over axes and groups
for ax, (grp_name, grp) in zip(axes, grouper):
# Plot from each grp DataFrame on ax from axes
sns.lineplot(data=grp, x='x', y='y', ax=ax, label=grp_name)
plt.show()
Ссылки для построения подзаголовков:
2.а
relplot
параметрrow
илиcol
можно использовать для создания подстрок аналогично тому, какhue
создается несколько строк. Это вернетseaborn.FacetGrid
, поэтому последующая обработка будет отличатьсяlineplot
от той, которая возвращает matplotlib.axes.Оси
2.в
groupby
для создания итераций, которые можно использовать для построения подзаголовков.pyplot.subplots
для создания сюжетных линий для построения графика.groupby.ngroup
для подсчета количества групп.zip
для одновременного перебора осей и групп.sns.lineplot
к заговору. Примечаниеlabel
необходимо иметь легенды.grp_name
содержит текущий ключ, который является общим вgrp
текущем кадре данных.pyplot.show
для отображения.
Вариант построения графика 3 (отдельные участки):
# Plot from each grp DataFrame in it's own plot
for grp_name, grp in plot_df.groupby('name'):
fig, ax = plt.subplots()
sns.lineplot(data=grp, x='x', y='y', ax=ax)
ax.set_title(grp_name)
fig.show()
Ссылки для построения отдельных участков:
groupby
чтобы создать итерацию, которую можно использовать для построения каждого имени отдельно.pyplot.subplots
чтобы создать отдельный участок для построения графика.sns.lineplot
к заговору. Примечаниеlabel
необходимо иметь легенды.grp_name
содержит текущий ключ, который является общим вgrp
текущем кадре данных.pyplot.show
для отображения.
Комментарии:
1. Фантастический ответ, разобрать все было чрезвычайно полезно. Одной из моих проблем была попытка определить более полезный способ организации данных в первую очередь. Спасибо.
2. Очень приятно, но я бы предложил использовать
relplot(..., col='name')
для подзаголовков, так как у вас уже есть данные в длинной форме.3. Спасибо @mwaskom обновил мой ответ.
relplot
это отличное дополнение.4. @HenryEcker, Возможно, не имеет отношения к исходному вопросу, но у меня все еще возникают проблемы с преобразованием строковых значений в целые. Проверка типа данных до
plot_df.loc[:, ['x', 'y']] = plot_df.loc[:, ['x', 'y']].astype(int)
показывает, что «x» и » y » являются объектами, а затем они не преобразуются (все еще объекты).5. Я действительно не уверен…
astype
не проваливается бесшумно. Если у него возникли проблемы с преобразованием чего-либо, это должно вызвать ошибку.
Ответ №2:
Из того, что я понял, это то, чего ты хочешь.
df = pd.DataFrame()
df['name'] = ['joe', 'sue']
df['x'] = ['0,1,5', '0,2,8']
df['y'] = ['0,3,8', '1,9,5']
df['newx'] = df['x'].str.split(',')
df['newy'] = df['y'].str.split(',')
for i in range(len(df)):
sns.lineplot(x=df.loc[i, 'newx'], y=df.loc[i, 'newy'])
plt.legend(df['name'])