Python: Построение значений, разделенных запятыми, в двух разных столбцах одной строки (Панды)

#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 :

  1. DataFrame.loc для подмножества фрейма данных
  2. Series.str.split чтобы разделить значения, разделенные запятыми, на список
  3. DataFrame.explode для масштабирования фрейма данных на основе итеративного в x
  4. DataFrame.reset_index чтобы снова сделать индекс уникальным после взрыва
  5. Series.explode чтобы увеличить масштаб списков в серии y .
  6. Series.reset_index чтобы снова сделать индекс уникальным после взрыва
  7. DataFrame.astype поскольку значения изначально являются строками, простого разделения и взрыва недостаточно. Нужно будет преобразовать их в числовой тип, чтобы они могли правильно строить

Построение графика (Вариант 1)

 # Plot with hue set to name.
sns.lineplot(data=plot_df, x='x', y='y', hue='name')
plt.show()
 

участок 1

Ссылки для построения отдельных строк:

  1. sns.lineplot к заговору. Обратите внимание на hue аргумент для создания отдельных строк на основе name .
  2. pyplot.show для отображения.

Построение (Вариант 2.а) Подзаголовков:

 sns.relplot(data=plot_df, x='x', y='y', col='name', kind='line')
plt.tight_layout()
plt.show()
 

график 2.повторная схема (сетка фасетов)

Построение (Вариант 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.b подзаголовки (matplotlib)

Ссылки для построения подзаголовков:

2.а

  1. relplot параметр row или col можно использовать для создания подстрок аналогично тому, как hue создается несколько строк. Это вернет seaborn.FacetGrid , поэтому последующая обработка будет отличаться lineplot от той, которая возвращает matplotlib.axes.Оси

2.в

  1. groupby для создания итераций, которые можно использовать для построения подзаголовков.
  2. pyplot.subplots для создания сюжетных линий для построения графика.
  3. groupby.ngroup для подсчета количества групп.
  4. zip для одновременного перебора осей и групп.
  5. sns.lineplot к заговору. Примечание label необходимо иметь легенды. grp_name содержит текущий ключ, который является общим в grp текущем кадре данных.
  6. 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()
 
джо заговор майк заговор подать в суд на участок
джо заговор майк заговор подать в суд на участок

Ссылки для построения отдельных участков:

  1. groupby чтобы создать итерацию, которую можно использовать для построения каждого имени отдельно.
  2. pyplot.subplots чтобы создать отдельный участок для построения графика.
  3. sns.lineplot к заговору. Примечание label необходимо иметь легенды. grp_name содержит текущий ключ, который является общим в grp текущем кадре данных.
  4. 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'])