Python 在Plotly直方图中,如何使每个动画帧成为数据帧中的一行?

Python 在Plotly直方图中,如何使每个动画帧成为数据帧中的一行?,python,pandas,plotly,plotly-python,Python,Pandas,Plotly,Plotly Python,我有以下数据帧: month stories comments comment_authors story_authors 0 2006-10 49 12 4 16 1 2007-02 564 985 192 163 2 2007-03 1784 4521 445

我有以下数据帧:

       month  stories  comments  comment_authors  story_authors
0    2006-10       49        12                4             16
1    2007-02      564       985              192            163
2    2007-03     1784      4521              445            287
我试图构建一个绘图直方图,其中每个
故事
评论
评论作者
故事作者
列有四个分类箱(x轴),计数(y轴)是特定
月份
的给定数量(即特定行)。然后,我尝试使用
animation\u frame
animation\u group
根据月份设置直方图动画

例如,
month=2006-10
的第一个直方图如下所示:

50 |  ____
45 | |    |
40 | |    |
35 | |    |
30 | |    |
25 | |    |
20 | |    |
15 | |    |                               ____
10 | |    |    ____                      |    |
 5 | |    |   |    |    ______           |    |
 0  ----------------------------------------------------
     stories  comments  comment_authors  story_authors
在下一个动画帧的柱状图中,它将读取
故事
评论
评论作者
故事作者
列中2007-02年
月份的值

这是否可以在Plotly中构建?有没有比
px.Histogram
更好的图形,比如
px.Bar
?我尝试将列放在x轴上,并使用月份作为动画帧,但这会将列堆叠到直方图上的一个箱子中,并使用整个列的计数,而不是特定行的值

histogram = dcc.Graph(figure=px.histogram(
    df, x=['stories', 'comments', 'comment_authors', 'story_authors'],
    animation_frame='month', animation_group='month'
))

不可能用您呈现的数据绘制直方图。你能做的最好的是一个条形图,你可以用一个时间序列来制作它的动画。我已经修改了官方文件中的样本,以符合您的数据

import pandas as pd
import numpy as np
import io

data = '''
      month  stories  comments  comment_authors  story_authors
0    2006-10       49        12                4             16
1    2007-02      564       985              192            163
2    2007-03     1784      4521              445            287
'''

df = pd.read_csv(io.StringIO(data), delim_whitespace=True)
df.set_index('month', inplace=True)
dfs = df.unstack().reset_index()
dfs.columns = ['category', 'month', 'value']
import plotly.express as px

fig = px.bar(dfs, x='category', y='value', color='category', animation_frame='month', range_y=[0,dfs['value'].max()])

fig.show()

不可能用显示的数据绘制直方图。你能做的最好的是一个条形图,你可以用一个时间序列来制作它的动画。我已经修改了官方文件中的样本,以符合您的数据

import pandas as pd
import numpy as np
import io

data = '''
      month  stories  comments  comment_authors  story_authors
0    2006-10       49        12                4             16
1    2007-02      564       985              192            163
2    2007-03     1784      4521              445            287
'''

df = pd.read_csv(io.StringIO(data), delim_whitespace=True)
df.set_index('month', inplace=True)
dfs = df.unstack().reset_index()
dfs.columns = ['category', 'month', 'value']
import plotly.express as px

fig = px.bar(dfs, x='category', y='value', color='category', animation_frame='month', range_y=[0,dfs['value'].max()])

fig.show()

@r-初学者的答案是最好的方法,但我想与大家分享如何手动操作,以防将来有人发现这个问题(这样我的辛劳就不会白费了)。下面是在@r-初学者回答之前我是如何手动完成的

# build list of frames for animation
cols = ['stories', 'comments', 'comment_authors', 'story_authors']
frames = list(map(
    lambda index: go.Frame(
        data=[go.Bar(
            x=cols,
            y=list(map(lambda col: df.iloc[index][col], cols))
        )]
    ), range(len(df.index))
))

# compute chart height
m = max(list(filter(
    lambda m: not isinstance(m, str), list(df.max())
))) * 1.1

# compute animation steps for slider
steps = list(map(
    lambda index: {
        'args': [
            [str(df.iloc[index]['month'])],
            {
                'frame': {'duration': 300, 'redraw': False},
                'mode': 'immediate',
                'transition': {'duration': 300}
            }
        ],
        'label': str(df.iloc[index]['month']),
        'method': 'animate'
    }, range(len(df.index))
))

# metadata for slider element
sliders_dict = {
    "active": 0,
    "yanchor": "top",
    "xanchor": "left",
    "currentvalue": {
        "font": {"size": 20},
        "prefix": "Month: ",
        "visible": True,
        "xanchor": "right"
    },
    "transition": {"duration": 300, "easing": "cubic-in-out"},
    "pad": {"b": 10, "t": 50},
    "len": 0.9,
    "x": 0.1,
    "y": 0,
    "steps": steps
}

# histogram figure - actually a bar chart
histogram = dcc.Graph(id='counts-histogram', figure=go.Figure(
    data=frames[0].data[0],
    layout=go.Layout(
        yaxis=dict(range=[0, m], autorange=False),
        updatemenus=[
            {
                "buttons": [
                    {
                        "args": [None, {"frame": {"duration": 500, "redraw": False},
                                        "fromcurrent": True, "transition": {"duration": 300,
                                                                            "easing": "quadratic-in-out"}}],
                        "label": "Play",
                        "method": "animate"
                    },
                    {
                        "args": [[None], {"frame": {"duration": 0, "redraw": False},
                                          "mode": "immediate",
                                          "transition": {"duration": 0}}],
                        "label": "Pause",
                        "method": "animate"
                    }
                ],
                "direction": "left",
                "pad": {"r": 10, "t": 87},
                "showactive": False,
                "type": "buttons",
                "x": 0.1,
                "xanchor": "right",
                "y": 0,
                "yanchor": "top"
            }
        ],
        sliders = [sliders_dict]
    ),
    frames=frames
))

@r-初学者的答案是最好的方法,但我想与大家分享如何手动操作,以防将来有人发现这个问题(这样我的辛劳就不会白费了)。下面是在@r-初学者回答之前我是如何手动完成的

# build list of frames for animation
cols = ['stories', 'comments', 'comment_authors', 'story_authors']
frames = list(map(
    lambda index: go.Frame(
        data=[go.Bar(
            x=cols,
            y=list(map(lambda col: df.iloc[index][col], cols))
        )]
    ), range(len(df.index))
))

# compute chart height
m = max(list(filter(
    lambda m: not isinstance(m, str), list(df.max())
))) * 1.1

# compute animation steps for slider
steps = list(map(
    lambda index: {
        'args': [
            [str(df.iloc[index]['month'])],
            {
                'frame': {'duration': 300, 'redraw': False},
                'mode': 'immediate',
                'transition': {'duration': 300}
            }
        ],
        'label': str(df.iloc[index]['month']),
        'method': 'animate'
    }, range(len(df.index))
))

# metadata for slider element
sliders_dict = {
    "active": 0,
    "yanchor": "top",
    "xanchor": "left",
    "currentvalue": {
        "font": {"size": 20},
        "prefix": "Month: ",
        "visible": True,
        "xanchor": "right"
    },
    "transition": {"duration": 300, "easing": "cubic-in-out"},
    "pad": {"b": 10, "t": 50},
    "len": 0.9,
    "x": 0.1,
    "y": 0,
    "steps": steps
}

# histogram figure - actually a bar chart
histogram = dcc.Graph(id='counts-histogram', figure=go.Figure(
    data=frames[0].data[0],
    layout=go.Layout(
        yaxis=dict(range=[0, m], autorange=False),
        updatemenus=[
            {
                "buttons": [
                    {
                        "args": [None, {"frame": {"duration": 500, "redraw": False},
                                        "fromcurrent": True, "transition": {"duration": 300,
                                                                            "easing": "quadratic-in-out"}}],
                        "label": "Play",
                        "method": "animate"
                    },
                    {
                        "args": [[None], {"frame": {"duration": 0, "redraw": False},
                                          "mode": "immediate",
                                          "transition": {"duration": 0}}],
                        "label": "Pause",
                        "method": "animate"
                    }
                ],
                "direction": "left",
                "pad": {"r": 10, "t": 87},
                "showactive": False,
                "type": "buttons",
                "x": 0.1,
                "xanchor": "right",
                "y": 0,
                "yanchor": "top"
            }
        ],
        sliders = [sliders_dict]
    ),
    frames=frames
))

谢谢,这比我的手动方法简单得多谢谢,这比我的手动方法简单得多