#python #pandas #matplotlib #plot #seaborn
#python #pandas #matplotlib #график #seaborn
Вопрос:
Мне нужно сравнить разные наборы ежедневных данных между 4 сменами (категориальный / групповой), используя гистограммы и линейные графики. Я искал везде и не нашел рабочего решения для этого, которое не включало бы создание новых сводных точек и тому подобное.
Я использовал оба, matplotlib и seaborn, и, хотя я могу использовать один или другой (разные цветные полосы / линии для каждой смены), как только я включаю другой, либо один исчезает, либо происходят другие аномалии, как показано только в одной точке графика. Я просмотрел все, и есть решения для представления одной серии данных на обоих типах диаграмм, но ни одно из них не относится к нескольким категориям или не сгруппировано для обоих.
Пример данных:
report_date wh_id shift Head_Count UTL_R
3/17/19 55 A 72 25%
3/18/19 55 A 71 10%
3/19/19 55 A 76 20%
3/20/19 55 A 59 33%
3/21/19 55 A 65 10%
3/22/19 55 A 54 20%
3/23/19 55 A 66 14%
3/17/19 55 1 11 10%
3/17/19 55 2 27 13%
3/17/19 55 3 18 25%
3/18/19 55 1 23 100%
3/18/19 55 2 16 25%
3/18/19 55 3 12 50%
3/19/19 55 1 28 10%
3/19/19 55 2 23 50%
3/19/19 55 3 14 33%
3/20/19 55 1 29 25%
3/20/19 55 2 29 25%
3/20/19 55 3 10 50%
3/21/19 55 1 17 20%
3/21/19 55 2 29 14%
3/21/19 55 3 30 17%
3/22/19 55 1 12 14%
3/22/19 55 2 10 100%
3/22/19 55 3 17 14%
3/23/19 55 1 16 10%
3/23/19 55 2 11 100%
3/23/19 55 3 13 10%
tm_daily_df = pd.read_csv('fg_TM_Daily.csv')
tm_daily_df = tm_daily_df.set_index('report_date')
fig2, ax2 = plt.subplots(figsize=(12,8))
ax3 = ax2.twinx()
group_obj = tm_daily_df.groupby('shift')
g = group_obj['Head_Count'].plot(kind='bar', x='report_date', y='Head_Count',ax=ax2,stacked=False,alpha = .2)
g = group_obj['UTL_R'].plot(kind='line',x='report_date', y='UTL_R', ax=ax3,marker='d', markersize=12)
plt.legend(tm_daily_df['shift'].unique())
Этот код приблизил меня к тому, что я смог получить. Обратите внимание, что даже с stacked = False
они все еще сложены. Я изменил настройку на True, и ничего не изменилось.
Все, что мне нужно, это чтобы столбцы были рядом друг с другом с одинаковой цветовой схемой, представляющей сдвиг
График:
Комментарии:
1. Спасибо за публикацию на SO и добро пожаловать на сайт. Чтобы убедиться, что я понимаю ваш вопрос, вы пытаетесь построить
head_count
как столбчатую диаграмму, где каждый стах представляет сдвиг, иUTL_R
как линейный график, где каждая строка представляет сдвиг?2. Спасибо за приветствие! Я хотел, чтобы численность персонала не была загружена, так как я хочу сравнить численность персонала в каждой смене каждый день (A для всего сайта / всего количества смен). Каждый день необходимо иметь 4 столбца рядом друг с другом, но, глядя на то, насколько чисто это выглядит, я бы не возражал, чтобы они были сложены (но не целые значения друг над другом, вместо этого больше похоже на то, что вы видите на картинке, каждый столбец находится друг над другом, начиная с y = 0), если вы можете различать каждый сдвиг в стеке, и если они имеют одинаковые цвета, назначенные сдвигам на линейном графике.
Ответ №1:
Вот два решения (сложенные и неупакованные). На основе ваших вопросов мы будем:
- постройте
Head_Count
по левой оси y иUTL_R
по правой оси y. report_date
будет нашей осью xshift
будет представлять оттенок нашего графика.
В сложенной версии используется pandas
функция построения графиков по умолчанию, а в неупакованной версии — seaborn
.
Редактировать
По вашему запросу я добавил 100%-ный сложенный график. Хотя это не совсем то, что вы спросили в комментарии, тип графика, который вы спросили, может создать некоторую путаницу при чтении (значения основаны на верхней строке стека или ширине стека). Альтернативным решением может быть использование 100%-ного сложенного графика.
Сложенный
import pandas as pd
import seaborn as sns
import matplotlib.pyplot as plt
dfg = df.set_index(['report_date', 'shift']).sort_index(level=[0,1])
fig, ax = plt.subplots(figsize=(12,6))
ax2 = ax.twinx()
dfg['Head_Count'].unstack().plot.bar(stacked=True, ax=ax, alpha=0.6)
dfg['UTL_R'].unstack().plot(kind='line', ax=ax2, marker='o', legend=None)
ax.set_title('My Graph')
plt.show()
Уложено 100%
import pandas as pd
import seaborn as sns
import matplotlib.pyplot as plt
dfg = df.set_index(['report_date', 'shift']).sort_index(level=[0,1])
# Create `Head_Count_Pct` column
for date in dfg.index.get_level_values('report_date').unique():
for shift in dfg.loc[date, :].index.get_level_values('shift').unique():
dfg.loc[(date, shift), 'Head_Count_Pct'] = dfg.loc[(date, shift), 'Head_Count'].sum() / dfg.loc[(date, 'A'), 'Head_Count'].sum()
fig, ax = plt.subplots(figsize=(12,6))
ax2 = ax.twinx()
pal = sns.color_palette("Set1")
dfg[dfg.index.get_level_values('shift').isin(['1','2','3'])]['Head_Count_Pct'].unstack().plot.bar(stacked=True, ax=ax, alpha=0.5, color=pal)
dfg['UTL_R'].unstack().plot(kind='line', ax=ax2, marker='o', legend=None, color=pal)
ax.set_title('My Graph')
plt.show()
Распакованный
import pandas as pd
import seaborn as sns
import matplotlib.pyplot as plt
dfg = df.set_index(['report_date', 'shift']).sort_index(level=[0,1])
fig, ax = plt.subplots(figsize=(15,6))
ax2 = ax.twinx()
sns.barplot(x=dfg.index.get_level_values('report_date'),
y=dfg.Head_Count,
hue=dfg.index.get_level_values('shift'), ax=ax, alpha=0.7)
sns.lineplot(x=dfg.index.get_level_values('report_date'),
y=dfg.UTL_R,
hue=dfg.index.get_level_values('shift'), ax=ax2, marker='o', legend=None)
ax.set_title('My Graph')
plt.show()
РЕДАКТИРОВАТЬ # 2
Вот график, который вы просили во второй раз (сложенный, но стек n 1 не начинается там, где заканчивается стек n).
Это немного сложнее, поскольку нам нужно сделать несколько вещей: — нам нужно вручную назначить наш цвет нашему shift
в нашем df — после того, как мы назначим наши цвета, мы пройдемся по каждому диапазону дат и 1) отсортируем или Head_Count
значения по убыванию (так, чтобы наш самый большой пакет был сзади, когда мы строим график), и 2) построим данные и назначим цвет каждому stacj — Затем мы можем создать нашу вторую ось y и построить наши UTL_R
значения — Затем нам нужно назначить правильный цвет для наши легендарные этикетки
import pandas as pd
import seaborn as sns
import matplotlib.pyplot as plt
def assignColor(shift):
if shift == 'A':
return 'R'
if shift == '1':
return 'B'
if shift == '2':
return 'G'
if shift == '3':
return 'Y'
# map a color to a shift
df['color'] = df['shift'].apply(assignColor)
fig, ax = plt.subplots(figsize=(15,6))
# plot our Head_Count values
for date in df.report_date.unique():
d = df[df.report_date == date].sort_values(by='Head_Count', ascending=False)
y = d.Head_Count.values
x = date
color = d.color
b = plt.bar(x,y, color=color)
# Plot our UTL_R values
ax2 = ax.twinx()
sns.lineplot(x=df.report_date, y=df.UTL_R, hue=df['shift'], marker='o', legend=None)
# Assign the color label color to our legend
leg = ax.legend(labels=df['shift'].unique(), loc=1)
legend_maping = dict()
for shift in df['shift'].unique():
legend_maping[shift] = df[df['shift'] == shift].color.unique()[0]
i = 0
for leg_lab in leg.texts:
leg.legendHandles[i].set_color(legend_maping[leg_lab.get_text()])
i = 1
Комментарии:
1. Эта неупакованная диаграмма — это именно то, что я хотел сделать, когда начинал. Итак, есть ли в любом случае, что столбцы могут быть «поверх» друг друга, но не сложены? В примере графического изображения, который я опубликовал, столбцы наложены друг на друга, но они не сложены. Так это выглядит намного чище. Возможно ли это?
2. @ricsilo, я не уверен, что этот тип графика будет наиболее репрезентативным для значения, поскольку читателю придется разбирать, является ли значение, представленное стеком, верхним стеком или шириной стека. Для вашей конкретной проблемы неупакованный график кажется наиболее подходящим. Хотя, если вы хотите пойти по сложенному маршруту, вы могли бы использовать 100% сложенный график.
3.Я делаю меньше, и значения в 100% сложенном графике не будут точными (поскольку A представляет 100% сдвига). Хотя, судя по вашим данным, это, похоже, не так (т. Е.
3/17/2019
A=72
, Но1 2 3 = 58
. В любом случае, я добавил код для вас, чтобы показать, как вы можете его создать — если вам это когда-нибудь понадобится.4. Итак, если это 100% с накоплением, есть ли способ, которым я могу умножить соотношение на значение A, чтобы вместо 0 -1 значения были 0-80 (или независимо от численности персонала)? И, хороший момент, но предоставленные мной данные были рандомизированы, чтобы не предоставлять личные данные. Я понимаю, что это не складывается, но реальные данные складываются….
5. Именно то, что я хотел! Я также искал решение, использующее c = df[‘color’].apply (лямбда x: цвета [x] путем сопоставления столбца цвета из shift и его применения.
Ответ №2:
Как насчет этого?
tm_daily_df['UTL_R'] = tm_daily_df['UTL_R'].str.replace('%', '').astype('float') / 100
pivoted = tm_daily_df.pivot_table(values=['Head_Count', 'UTL_R'],
index='report_date',
columns='shift')
pivoted
# Head_Count UTL_R
# shift 1 2 3 A 1 2 3 A
# report_date
# 3/17/19 11 27 18 72 0.10 0.13 0.25 0.25
# 3/18/19 23 16 12 71 1.00 0.25 0.50 0.10
# 3/19/19 28 23 14 76 0.10 0.50 0.33 0.20
# 3/20/19 29 29 10 59 0.25 0.25 0.50 0.33
# 3/21/19 17 29 30 65 0.20 0.14 0.17 0.10
# 3/22/19 12 10 17 54 0.14 1.00 0.14 0.20
# 3/23/19 16 11 13 66 0.10 1.00 0.10 0.14
fig, ax = plt.subplots()
pivoted['Head_Count'].plot.bar(ax=ax)
pivoted['UTL_R'].plot.line(ax=ax, legend=False, secondary_y=True, marker='D')
ax.legend(loc='upper left', title='shift')