简体   繁体   English

Plotly:如何使用 plotly express 为具有多个组的条形图设置动画?

[英]Plotly: How to animate a bar chart with multiple groups using plotly express?

I have a dataframe that looks like this:我有一个看起来像这样的数据框:

在此处输入图片说明

I want to have one bar for old freq and one for new freq.我想要一个用于旧频率和一个用于新频率的条。 Currently I have graph that looks like this:目前我的图表如下所示:

在此处输入图片说明

This is what the code looks like:这是代码的样子:

freq_df['date'] = pd.to_datetime(freq_df['date'])
freq_df['hour'] = freq_df['hour'].astype(str)

fig = px.bar(freq_df, x="hour", y="old freq",hover_name = "date",
  animation_frame= freq_df.date.dt.day)
fig.update_layout(transition = {'duration': 2000})

How do I add another bar?我如何添加另一个酒吧?

Explanation about DF:关于DF的说明:

It has frequencies relevant to each hour in a specific date.它具有与特定日期中的每个小时相关的频率。

Edit: One approach could be to create a category column and add old and new freq and assign values in another freq column.编辑:一种方法可能是创建一个类别列并添加旧的和新的频率并在另一个频率列中分配值。 How do I do that :p ?我该怎么做:p?

Edit: Here is the DF编辑:这是DF

    ,date,hour,old freq,new freq
43,2020-09-04,18,273,224.0
44,2020-09-04,19,183,183.0
45,2020-09-04,20,99,111.0
46,2020-09-04,21,130,83.0
47,2020-09-04,22,48,49.0
48,2020-09-04,23,16,16.0
49,2020-09-05,0,8,6.0
50,2020-09-05,1,10,10.0
51,2020-09-05,2,4,4.0
52,2020-09-05,3,7,7.0
53,2020-09-05,4,25,21.0
54,2020-09-05,5,114,53.0
55,2020-09-05,6,284,197.0
56,2020-09-05,7,343,316.0
57,2020-09-05,8,418,419.0
58,2020-09-05,9,436,433.0
59,2020-09-05,10,469,396.0
60,2020-09-05,11,486,300.0
61,2020-09-05,12,377,140.0
62,2020-09-05,13,552,103.0
63,2020-09-05,14,362,117.0
64,2020-09-05,15,512,93.0
65,2020-09-05,16,392,41.0
66,2020-09-05,17,268,31.0
67,2020-09-05,18,223,30.0
68,2020-09-05,19,165,24.0
69,2020-09-05,20,195,15.0
70,2020-09-05,21,90,
71,2020-09-05,22,46,1.0
72,2020-09-05,23,17,1.0

The answer in two steps:分两步回答:

1. Perform a slight transformation of your data using pd.wide_to_long : 1.使用pd.wide_to_long对数据进行轻微转换:

df_long = pd.wide_to_long(freq_df, stubnames='freq',
                          i=['date', 'hour'], j='type',
                          sep='_', suffix='\w+').reset_index()

2. Plot two groups of bar traces using: 2. 使用以下方法绘制两组条形轨迹:

fig1 = px.bar(df_long, x='hour', y = 'freq', hover_name = "date", color='type',
            animation_frame= 'date', barmode='group')

This is the result for the first frame:这是第一帧的结果:

在此处输入图片说明

This is the result for the second frame when you run the animation:这是运行动画时第二帧的结果:

在此处输入图片说明


The details:细节:

If I understand your question correctly, you'd like to animate a bar chart where you've got one bar for each hour for your two frequencies freq_old and freq_new like this:如果我正确理解您的问题,您想为条形图制作动画,其中每小时为两个频率freq_oldfreq_new设置一个条形图,如下所示:

在此处输入图片说明

If that's the case, then you sample data is no good since your animation critera is hour per date and you've only got four observations (hours) for 2020-09-04 and then 24 observations for 2020-09-05 .如果是这种情况,那么您的样本数据就不好了,因为您的动画标准是每个date hour数,并且您只有2020-09-04四个观察值(小时)和2020-09-04 24 个观察2020-09-05 But don't worry, since your question triggered my interest I just as well made some sample data that will in fact work the way you seem to want them to.但别担心,既然你的问题引起了我的兴趣,我也制作了一些示例数据,这些数据实际上会按照你希望的方式工作。

The only real challenge is that px.bar will not accept y= [freq_old, freq_new] , or something to that effect, to build your two bar series of different categories for you.唯一真正的挑战是px.bar不会接受y= [freq_old, freq_new]或类似的东西来为您构建不同类别的两个条形系列。 But you can make px.bar build two groups of bars by providing a color argument.但是您可以通过提供color参数使px.bar构建两组条形。 However, you'll need a column to identify your different freqs like this:但是,您需要一列来识别您的不同freqs如下所示:

0    new
1    old
2    new
3    old
4    new
5    old
6    new
7    old
8    new
9    old

In other words, you'll have to transform your dataframe, which originally has a wide format, to a long format like this:换句话说,您必须将最初具有宽格式的数据帧转换为长格式,如下所示:

    date        hour    type    day freq
0   2020-01-01  0       new     1   7.100490
1   2020-01-01  0       old     1   2.219932
2   2020-01-01  1       new     1   7.015528
3   2020-01-01  1       old     1   8.707323
4   2020-01-01  2       new     1   7.673314
5   2020-01-01  2       old     1   2.067192
6   2020-01-01  3       new     1   9.743495
7   2020-01-01  3       old     1   9.186109
8   2020-01-01  4       new     1   3.737145
9   2020-01-01  4       old     1   4.884112

And that's what this snippet does:这就是这个片段的作用:

df_long = pd.wide_to_long(freq_df, stubnames='freq',
                          i=['date', 'hour'], j='type',
                          sep='_', suffix='\w+').reset_index()

stubnames uses a prefix to identify the columns you'd like to stack into a long format. stubnames使用前缀来标识您想要堆叠为长格式的列。 And that's why I've renamed new_freq and old_freq to freq_new and freq_old , respectively.这就是我将freq_newfreq_old分别重命名为new_freqold_freq的原因。 j='type' simply takes the last parts of your cartegory names using sep='_' and produces the column that we need to tell the freqs from eachother: j='type'简单地使用sep='_'获取类别名称的最后部分,并生成我们需要告诉彼此频率的列:

type
old
new
old
...

suffix='\\w+' tells pd.wide_to_long that we're using non-integers as suffixes. suffix='\\w+'告诉pd.wide_to_long我们使用非整数作为后缀。 And that's it!就是这样!

Complete code:完整代码:

# imports
import plotly.express as px
import plotly.graph_objects as go
import pandas as pd
import numpy as np
import random

# sample data
observations = 24*5
np.random.seed(5); cols = list('a')
freq_old = np.random.uniform(low=-1, high=1, size=observations).tolist()
freq_new = np.random.uniform(low=-1, high=1, size=observations).tolist()
date = [t[:10] for t in pd.date_range('2020', freq='H', periods=observations).format()]
hour = [int(t[11:13].lstrip()) for t in pd.date_range('2020', freq='H', periods=observations).format()]

# sample dataframe of a wide format such as yours
freq_df=pd.DataFrame({'date': date, 
                'hour':hour,
                 'freq_new':freq_new,
                 'freq_old':freq_old})
freq_df['day']=pd.to_datetime(freq_df['date']).dt.day

# attempt to make my random data look a bit
# like your real world data.
# but don't worry too much about that...
freq_df.freq_new = abs(freq_df.freq_new.cumsum())
freq_df.freq_old = abs(freq_df.freq_old.cumsum())

# sample dataframe of a long format that px.bar likes
df_long = pd.wide_to_long(freq_df, stubnames='freq',
                          i=['date', 'hour'], j='type',
                          sep='_', suffix='\w+').reset_index()

# plotly express bar chart with multiple bar groups.
fig = px.bar(df_long, x='hour', y = 'freq', hover_name = "date", color='type',
            animation_frame= 'date', barmode='group')

# set up a sensible range for the y-axis
fig.update_layout(yaxis=dict(range=[df_long['freq'].min()*0.8,df_long['freq'].max()*1.2]))
fig.show()

I was able to create the bars for both the old and new frequencies, however using a separate plot for each day (Plotly Express Bar Charts don't seem to have support for multiple series).我能够为旧的和新的频率创建条形图,但是每天使用一个单独的图(Plotly Express 条形图似乎不支持多个系列)。 Here is the code for doing so:这是这样做的代码:

# Import packages
import pandas as pd
import numpy as np

import plotly.graph_objs as go
import plotly
import plotly.express as px
from plotly.offline import init_notebook_mode, plot, iplot, download_plotlyjs
init_notebook_mode(connected=True)
plotly.offline.init_notebook_mode(connected=True)

# Start formatting data
allDates = np.unique(df.date)
numDates = allDates.shape[0]
print(numDates)

for i in range(numDates):
    df = original_df.loc[original_df.date == allDates[i]]
    
    oldFreqData = go.Bar(x=df["hour"].to_numpy(), y=df["old_freq"].to_numpy(), name="Old Frequency")
    newFreqData = go.Bar(x=df["hour"].to_numpy(), y=df["new_freq"].to_numpy(), name="New Frequency")

    fig = go.Figure(data=[oldFreqData,newFreqData])
    
    fig.update_layout(title=allDates[i])
    fig.update_xaxes(title='Hour')
    fig.update_yaxes(title='Frequency')
    
    fig.show()

where df is the dataframe DF from your question.其中df是您问题中的数据框 DF。

Here is the output:这是输出: Plotly_Multiple_Series

However, if you prefer the use of the animation frame from Plotly Express, you can have two separate plots: one for old frequencies and one for new using this code:但是,如果您更喜欢使用 Plotly Express 中的动画帧,您可以有两个单独的图:一个用于旧频率,另一个用于使用以下代码的新频率

# Reformat data
df = original_df
dates = pd.to_datetime(np.unique(df.date)).strftime('%Y-%m-%d')
numDays = dates.shape[0]
print(numDays)

hours = np.arange(0,24)
numHours = hours.shape[0]

allDates = []
allHours = []
oldFreqs = []
newFreqs = []
for i in range(numDays):
    for j in range(numHours): 
        allDates.append(dates[i])
        allHours.append(j)
        if (df.loc[df.date == dates[i]].loc[df.hour == j].shape[0] != 0):       # If data not missing
            oldFreqs.append(df.loc[df.date == dates[i]].loc[df.hour == j].old_freq.to_numpy()[0])
            newFreqs.append(df.loc[df.date == dates[i]].loc[df.hour == j].new_freq.to_numpy()[0])
        else:
            oldFreqs.append(0)
            newFreqs.append(0)
    
d = {'Date': allDates, 'Hour': allHours, 'Old_Freq': oldFreqs, 'New_Freq': newFreqs, 'Comb': combined}
df2 = pd.DataFrame(data=d)

# Create px plot with animation
fig = px.bar(df2, x="Hour", y="Old_Freq", hover_data=["Old_Freq","New_Freq"], animation_frame="Date")
fig.show()

fig2 = px.bar(df2, x="Hour", y="New_Freq", hover_data=["Old_Freq","New_Freq"], animation_frame="Date")
fig2.show()

and here is the plot from that code:这是该代码的情节: 情节动画帧

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM