如何在同一张图中绘制一条线和一个箱形图,并且在 python 中 x 轴是一个日期

问题描述 投票:0回答:1

我一直在尝试从两个数据框中绘制一个图,其中包含每月时间序列的排放值。第一个是数据框 (df1),其中包含以下列日期、年、月和排放。第二个数据框 (df2) 包含相同的列,但每个月包含不同的流量值。我想使用这两个数据帧在同一个图中绘制一个图。数据框 df1 必须是一个线图,x 轴为日期,y 轴为流量。第二个数据框 (df2) 必须是一个箱形图,x 轴作为日期,y 轴作为每个月的分组流量。

这是我测试过的代码

df1 = pd.DataFrame({
    'date': ['2023-04-01', '2023-03-01', '2023-02-01', '2023-01-01', '2022-12-01'],
    'year': [2023,2023,2023,2023,2022],
    'month': [4,3,2,1,12],
    'discharge': [10, 20, 30, 15, 25]
})

# Define the start and end dates
start_date = datetime(2023, 5, 1)
end_date = datetime(2023, 9, 1)

# Generate the date range
dates = pd.date_range(start=start_date, end=end_date, freq='MS')

# Define the values for the DataFrame
df2  = {'date': dates.repeat(10),
        'year': [d.year for d in dates] * 10,
        'month': [d.month for d in dates] * 10,
        'discharge': [i+1 for i in range(len(dates))] * 10}

# Plot 

fig, ax = plt.subplots()
ax.plot(df1['date'], df1['discharge'], label='Discharge');

# plot the box plot
df2.boxplot(column='discharge', by='month', positions=[df2['date'][2]], widths=10, ax=ax)

我收到这个错误

ValueError: List of boxplot statistics and 
位置
 values must have same the length

python matplotlib datetime line boxplot
1个回答
0
投票

错误的原因是因为

pos
对于
df2['date'][2]
只有一个值。但是,即使您使用 unique() 手动给出其他值,它也不会起作用。有几个问题。一是您需要对折线图和箱形图都使用日期时间。我假设您希望日期逐渐增加,并且两个图的 y 轴需要相同。

为此,您需要首先将每个点(对于两个地块)转换为一个整数,该整数是从两个地块的最早日期算起的天数。然后,将为每个点计算偏移量(从最早日期算起的天数)并绘制在整数轴上。灌封后,您需要将 x-ticks 改回日期格式...下面是代码,我提供了尽可能多的评论。希望这就是您要找的...

import datetime
df1 = pd.DataFrame({'date': ['2023-04-01', '2023-03-01', '2023-02-01', '2023-01-01', '2022-12-01'], 'year': [2023,2023,2023,2023,2022], 'month': [4,3,2,1,12], 'discharge': [10, 20, 30, 15, 25]})
df1['date']=pd.to_datetime(df1['date'])

# Define the start and end dates
start_date = datetime.datetime(2023, 5, 1)
end_date = datetime.datetime(2023, 9, 1)

# Generate the date range
dates = pd.date_range(start=start_date, end=end_date, freq='MS')

# Define the values for the DataFrame
df2  = pd.DataFrame({'date': dates.repeat(10), 'year': [d.year for d in dates] * 10, 'month': [d.month for d in dates] * 10, 'discharge': [i+1 for i in range(len(dates))] * 10})

## Get the earliest date in BOTH plots combined
begin=pd.concat([df1.date, pd.Series(df2.date.unique())]).min()

## Add columns linepos and boxpos to the dataframes to show offset from earliest date
df1['linepos']=(df1['date']-begin).dt.days
df2['boxpos']=(df2['date']-begin).dt.days

## Plot plots - note I am using boxpos and linepos, not dates for x-axis
ax=df2[['discharge', 'boxpos']].boxplot(by='boxpos', widths=4, positions=df2.boxpos.unique(), figsize=(20,7))
ax.plot(df1['linepos'], df1['discharge'], label='Discharge')

## Set x-lim to include both line and boxes
ax.set_xlim( [ min(df2.boxpos.min(), df1.linepos.min())-10, max(df2.boxpos.max(), df1.linepos.max()) + 10 ] )

## To change the x-axis ticks, get the list of all x-entries and sort
locs=(list(df2.boxpos.unique())+list(df1.linepos.unique()))
locs.sort()
ax.set_xticks(locs)

## To add labels get unique dates, sort them, convert to format you like and plot
ax.set_xticklabels(pd.concat([df1.date, pd.Series(df2.date.unique())]).sort_values().reset_index(drop=True).dt.strftime('%Y-%m-%d'), rotation=45 )

## Set x and y labels
ax.set_xlabel('Dates')
ax.set_ylabel('Discharge')

© www.soinside.com 2019 - 2024. All rights reserved.