我有一个数据框,其中包含 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
是否是最好的方法。
所需的输出:
问题:
如何创建数据框中每行N个最大列的堆积条形图?
答案 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)
,这应该为您提供类似于以下内容的信息:
答案 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()
结果图:
答案 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)