在数据框中创建每行N个最大列的堆积条形图

时间:2019-03-26 16:10:18

标签: python pandas matplotlib

我有一个数据框,其中包含 M 个日期的 N 列值。

我想绘制每个日期的3个最大值的堆叠条形图。

测试数据框:

import pandas
import numpy

data = {
    'A': [ 65, 54, 12, 14, 30, numpy.nan ],
    'B': [ 54, 47, 60, 34, 40, 35 ],
    'C': [ 34, 39, 57, 56, 48, numpy.nan ],
    'D': [ 20, 18, 47, 47, 35, 70 ]
}

df = pandas.DataFrame(index=pandas.date_range('2018-01-01', '2018-01-06').date,
                      data=data,
                      dtype=numpy.float64)
               A     B     C     D
2018-01-01  65.0  54.0  34.0  20.0
2018-01-02  54.0  47.0  39.0  18.0
2018-01-03  12.0  60.0  57.0  47.0
2018-01-04  14.0  34.0  56.0  47.0
2018-01-05  30.0  40.0  48.0  35.0
2018-01-06   NaN  35.0   NaN  70.0

提取每行3个最大值:

我发现nlargest可以用来提取3个最大的列及其每行的相应值:

for date,row in df.iterrows():
    top = row.nlargest(3)
    s = [f'{c}={v}' for c,v in top.iteritems()]
    print('{}: [ {} ]'.format(date, ', '.join(s)))
2018-01-01: [ A=65.0, B=54.0, C=34.0 ]
2018-01-02: [ A=54.0, B=47.0, C=39.0 ]
2018-01-03: [ B=60.0, C=57.0, D=47.0 ]
2018-01-04: [ C=56.0, D=47.0, B=34.0 ]
2018-01-05: [ C=48.0, B=40.0, D=35.0 ]
2018-01-06: [ D=70.0, B=35.0 ]

在堆积的条形图中绘制数据:

最后一步,获取上面的数据并绘制堆叠的条形图,使其看起来像下面的示例,我一直没有成功。

我什至不确定nlargest是否是最好的方法。

所需的输出:

stacked barchart example

问题:

如何创建数据框中每行N个最大列的堆积条形图?

3 个答案:

答案 0 :(得分:3)

从您的输入df开始:

top3_by_date = (
    # bring the date back as a column to use as a grouping var
    df.reset_index()
    # make a long DF of date/column/name value
    .melt(id_vars='index')
    # order DF by highest values first
    .sort_values('value', ascending=False)
    # group by the index and take the first 3 rows of each
    .groupby('index')
    .head(3)
    # pivot back so we've got an X & Y to chart...
    .pivot('index', 'variable')
    # drop the value level as we don't need that
    .droplevel(level=0, axis=1)
)

这给出了:

variable       A     B     C     D
index                             
2018-01-01  65.0  54.0  34.0   NaN
2018-01-02  54.0  47.0  39.0   NaN
2018-01-03   NaN  60.0  57.0  47.0
2018-01-04   NaN  34.0  56.0  47.0
2018-01-05   NaN  40.0  48.0  35.0
2018-01-06   NaN  35.0   NaN  70.0

然后您可以执行top3_by_date.plot.bar(stacked=True),这应该为您提供类似于以下内容的信息:

enter image description here

答案 1 :(得分:0)

这是可能的,但有点令人费解,因为您需要使用bottom来使同一日期的每个小节偏移较低的值。这样可以防止具有较高值的​​钢筋隐藏具有较低值的钢筋。

对于每一列(在条形图中代表一个系列),需要3个数组:

  • dates :具有该列值的日期(即:该列是3个最大值之一的日期)
  • values :此值与下一个较低的值之差
  • bottoms :下一个较低的值

构建阵列:

col_dates   = collections.defaultdict(list)
col_values  = collections.defaultdict(list)
col_bottoms = collections.defaultdict(list)

for date,row in df.iterrows():
    top = row.nlargest(3)
    for i,kv in enumerate(top.iteritems()):
        col, val = kv
        next_val = top.values[i+1] if i+1 < len(top.values) else 0

        col_dates  [col].append(date)
        col_values [col].append(val - next_val)
        col_bottoms[col].append(next_val)

绘制条形图:

fig = pyplot.figure(figsize=(20,10))
ax = fig.add_subplot(1,1,1)

for col,vals in col_values.items():
    dates   = col_dates[col]
    bottoms = col_bottoms[col]

    ax.bar(matplotlib.dates.date2num(dates), vals, width=.6, bottom=bottoms, label=col)
    ax.xaxis_date()

ax.legend(loc='best', fontsize='large')

pyplot.show()

结果图:

enter image description here

答案 2 :(得分:0)

您可以使用简单的apply进行此操作。它不会被矢量化,但是我认为它更清晰易读。在这种情况下,我用NaN填充了-np.inf,因为排序不适用于NaN值。

import pandas as pd
import numpy as np

data = {
    'A': [ 65, 54, 12, 14, 30, np.nan ],
    'B': [ 54, 47, 60, 34, 40, 35 ],
    'C': [ 34, 39, 57, 56, 48, np.nan ],
    'D': [ 20, 18, 47, 47, 35, 70 ]
}

df = pd.DataFrame(index=pd.date_range('2018-01-01', '2018-01-06').date,
                  data=data,
                  dtype=np.float64)

df.fillna(-np.inf, inplace=True)

def search_rows(row):
    return np.where(row.isin(sorted(row, reverse=True)[:3]), row, -np.inf)

df = df.apply(search_rows, axis=1)
df.plot.bar(stacked=True)