Python统计数组中多个区间内的频数并画直方图

目录

需求

给定一个数据数组或列表,如[1, 2, 2, 3, 5, 5, 7, 7.5, 9.5, 10] ,然后给定一个区间数组:[0, 4, 8, 10],要求依次统计数据数组在(0, 4](4, 8]以及(8, 10]三个区间内各自出现的频数,并画出直方图。

实现

首先随机生成0到10之间的100个整数:

data = np.random.randint(low=1, high=10 + 1, size=100, dtype='int')
print(data)

输出:

[ 8  7  1  9  8  7  8  7  7  8  3  6 10 10  9  2  2 10  1  2  2  6  6  2
  7  4  5  5  8  6  5  4  3  2  8  4  3  5 10  5  9  6  4  5  4  2  8  9
  7  8  7  9  7  4  2  7  7  6  3  3  6  7  8  1 10  7  7 10  4  4  6  4
  5  5 10  7  1  2  8  4  6  8  9  9  6  4  9  5  6  4  4 10  2  6  9  3
  4  8  4  9]

统计频数用pandascut函数可以很轻松地实现:

s = pd.cut(data, bins=[x for x in range(10 + 1)])
print(s.value_counts())

其中bins表示区间数据。如前文所述,如果bins=[0, 4, 8, 10],那么就是统计data(0, 4](4, 8]以及(8, 10]三个区间内各自出现的频数。

统计结果输出:

(0, 1]      9
(1, 2]      7
(2, 3]      7
(3, 4]      4
(4, 5]     12
(5, 6]     10
(6, 7]     14
(7, 8]      9
(8, 9]     18
(9, 10]    10
dtype: int64

然后获取频数:

values = s.value_counts().values
print(values)
[ 7  9 12 10  9 11 10 13  7 12]

获取横轴标签:

labels = [str(i) + '-' + str(i+1) for i in range(int(np.max(data)))]
print(labels)
['0-1', '1-2', '2-3', '3-4', '4-5', '5-6', '6-7', '7-8', '8-9', '9-10']

最后,生成dataframe并画图:

df = pd.DataFrame(values, index=labels)
df.plot(kind='bar', legend=False)
plt.xticks(rotation=0)
plt.ylabel('频数')
plt.xlabel('区间')
plt.show()

结果:
在这里插入图片描述

<think>好的,我现在要帮用户解决用matplotlib绘制直方图时纵轴显示百分比的问题。用户给出的数组是np.arange(0, 2, 0.4),也就是生成从0开始,步长0.4,直到不超过2的数组。那这个数组的具体数值是什么呢?先想一下,np.arange(0, 2, 0.4)生成的应该是0.0, 0.4, 0.8, 1.2, 1.6,对吧?因为下一步是2.0,但是不包括2,所以停在这里。所以数据点是这五个数。 用户想绘制这些数据的直方图,但纵轴要显示百分比而不是数量。那常规的直方图统计每个区间频数,所以需要将频数转换成百分比。这时候需要用到直方图的参数weights,或者是用density参数,但需要注意两者的区别。 首先,我应该回忆一下matplotlib中plt.hist的参数。density参数如果设为True,会将频数转换为密度,即面积和为1的分布,但用户想要的是百分比,也就是每个区间频数占总数的百分比。这时候可能需要手动计算每个bin的频数,再除以总数得到百分比,或者使用weights参数来调整。 比如,假设我们有n个数据点,每个数据点对应的权重是1/n,这样直方图的纵轴就会是各个bin中数据点的比例。所以设置weights参数为一个全为1/n的数组,长度和数据点相同,就可以达到百分比的效果。 那具体步骤应该是这样的:先生成数据,然后计算权重,再绘制直方图设置weights参数。同时,可能需要调整直方图的bins参数,确保分箱正确。因为用户的数据点是0.0, 0.4, 0.8, 1.2, 1.6,总共有五个点,可能默认的bins可能不合适,需要明确指定分箱边界,比如使用与数据点相同的步长? 不过,可能用户的数据比较少,直接使用默认分箱可能结果不够直观。比如,如果分箱的间隔是0.4的话,每个数据点刚好在各自的bin中。比如,0.0在0到0.4的bin,0.4在0.4到0.8的bin?或者要注意边界条件。可能需要明确指定bins的边界,比如bins=np.arange(0, 2.1, 0.4),这样每个数据点都会落在对应的区间里。 比如,数据点0.0会落在0.0-0.4的bin,0.4会落在0.4-0.8的bin,依此类推。这样的话,每个数据点各占一个bin,每个bin的频数为1,总共有5个数据点,所以每个bin的百分比是20%。这个时候,纵轴的显示应该是每个bin的高度为0.2(即20%)。 那具体代码怎么写呢?首先导入需要的库: import numpy as np import matplotlib.pyplot as plt 然后生成数据: data = np.arange(0, 2, 0.4) # data应该是array([0. , 0.4, 0.8, 1.2, 1.6]) 计算权重: weights = np.ones_like(data) / len(data) # 每个数据点的权重是1/5=0.2 然后绘制直方图,指定weights参数,设置bins: plt.hist(data, bins=np.arange(0, 2.1, 0.4), weights=weights) 或者,也可以使用density参数吗?如果设置density=True的话,得到的是密度估计,即每个bin的高度等于频数/(总数据数*bin宽度)。在这种情况下,bin宽度是0.4,所以每个bin的高度是1/(5*0.4)=0.5。这时候总面积是0.5*0.4*5=1,但用户想要的是百分比,所以density参数可能不合适。而用weights参数更直接,每个数据点贡献其权重到对应的bin,总权重之和为1,即100%。 所以正确的做法是使用weights参数,设置每个数据点的权重为1/len(data)。同时,可能需要明确设置bins的边界,以确保每个数据点都落在对应的区间里。 然后,绘制图形时,还可以设置纵轴的标签为“Percentage”或者“比例”,可能使用百分数格式化,比如用PercentFormatter来显示百分比符号。例如: from matplotlib.ticker import PercentFormatter plt.hist(data, bins=np.arange(0, 2.1, 0.4), weights=weights) plt.gca().yaxis.set_major_formatter(PercentFormatter(1)) # 将小数转换为百分比 plt.ylabel('Percentage') plt.xlabel('Value') plt.title('Percentage Histogram') 这样,纵轴的数值会以百分比的形式显示,比如0.2显示为20%。 不过,测试一下,如果数据刚好每个bin一个数据点的话,每个bin的高度是0.2,即20%。这样是否正确? 另外,如果用户的数据分布不同,比如有的bin有多个数据点,权重参数会自动累加,所以正确。 所以,综合起来,步骤是: 1. 生成数据数组。 2. 计算每个数据点的权重为1/总数据数。 3. 指定bins的边界,确保正确分箱。 4. 使用plt.hist,传入weights参数。 5. 使用PercentFormatter将y轴格式化为百分比。 现在检查是否所有步骤都正确。比如,当数据是五个点时,每个点各占一个bin,那么每个bin的权重是0.2,即20%。所以直方图的每个条的高度应该是20%。这样纵轴显示正确。 可能的错误点在于bins的设置。比如,如果不指定bins,默认是10个bin,这样数据会被分到不同的区间里,可能导致某些bin没有数据,或者多个数据点在一个bin。所以必须明确设置bins的间隔,以匹配数据点的分布。例如,用户的数据间隔是0.4,所以bins应该从0到2,步长0.4。但是np.arange(0, 2, 0.4)的终点是2,但是不包括,所以生成的数组是0, 0.4, 0.8, 1.2, 1.6。而分箱需要包括所有数据点,最大值是1.6,所以bins应该到2.0吗?或者到1.6+0.4=2.0。例如: bins = np.arange(0, 2.0 + 0.4, 0.4) 或者直接写成np.arange(0, 2.1, 0.4),这样生成的bins是0,0.4,0.8,1.2,1.6,2.0。但用户的数据最大是1.6,所以最后一个bin是1.6到2.0,但数据中没有这个区间的点,所以最后一个bin会是空的。所以实际分箱的边界应该到1.6+0.4=2.0吗? 不过,原来的数据点是0.0到1.6,步长0.4。所以在绘制直方图时,每个数据点位于每个区间的左闭右开区间。比如,0.4属于[0.4, 0.8)这个区间。所以,当分箱设置为0.0,0.4,0.8,1.2,1.6,2.0的时候,数据点0.0会进入第一个bin,0.4进入第二个,0.8进入第三个,1.2进入第四个,1.6进入第五个bin [1.6, 2.0),对吗? 是的。这样每个数据点都对应一个bin,所以每个bin的权重是0.2,即20%。这时候直方图会显示五个条,每个条的高度是20%。 所以最终的代码应该是这样的: import numpy as np import matplotlib.pyplot as plt from matplotlib.ticker import PercentFormatter data = np.arange(0, 2, 0.4) weights = np.ones(len(data)) / len(data) # 计算权重,每个数据点贡献1/5 plt.hist(data, bins=np.arange(0, 2.1, 0.4), weights=weights, edgecolor='black') # 设置分箱和权重 plt.gca().yaxis.set_major_formatter(PercentFormatter(1)) # 将y轴格式化为百分比 plt.xlabel('Value') plt.ylabel('Percentage') plt.title('Percentage Histogram') plt.show() 这样生成的直方图每个条的高度都是20%,纵轴显示为百分比。 可能用户需要注意分箱的设置是否正确,尤其是当数据范围变化时。此外,使用edgecolor参数可以让各个直方图条之间更清晰。 测试一下这段代码是否正确。当运行后,每个条的高度是否确实是20%。例如,如果数据是五个点,每个点对应一个bin,权重是0.2,所以每个条的高度是0.2,即20%。而PercentFormatter(1)会将数值乘以100显示为百分比,所以0.2变成20%。 是的,这样应该正确。所以最终回答需要详细说明这些步骤,给出代码示例。</think>要使用matplotlib绘制数组$[0.0, 0.4, 0.8, 1.2, 1.6]$的直方图将纵轴显示为百分比,可通过以下步骤实现: --- ### **步骤说明** 1. **数据准备** 生成数组:$$ \text{data} = \text{np.arange}(0, 2, 0.4) $$ 结果:$[0.0, 0.4, 0.8, 1.2, 1.6]$ 2. **权重计算** 每个数据点的权重设为$1/N$($N$为数据总数),使纵轴表示百分比: $$ \text{weights} = \frac{1}{\text{len(data)}} $$ 3. **分箱设置** 明确指定分箱边界`bins`,确保每个数据点单独成箱: $$ \text{bins} = \text{np.arange}(0, 2.1, 0.4) $$ 边界为:$[0.0, 0.4, 0.8, 1.2, 1.6, 2.0]$ 4. **绘图与格式** - 使用`plt.hist`时传入`weights`参数 - 通过`PercentFormatter`将纵轴格式化为百分比 --- ### **完整代码** ```python import numpy as np import matplotlib.pyplot as plt from matplotlib.ticker import PercentFormatter # 生成数据 data = np.arange(0, 2, 0.4) weights = np.ones(len(data)) / len(data) # 计算权重 # 绘制直方图 plt.hist( data, bins=np.arange(0, 2.1, 0.4), # 明确分箱边界 weights=weights, # 设置权重 edgecolor="black", # 添加边框 alpha=0.7 # 调整透明度 ) # 格式化纵轴为百分比 plt.gca().yaxis.set_major_formatter(PercentFormatter(1)) plt.xlabel("Value") plt.ylabel("Percentage") plt.title("Percentage Histogram") plt.show() ``` --- ### **效果说明** - **纵轴**:显示为百分比(如$20\%$表示该区间数据占比$1/5$)。 - **横轴**:每个分箱对应区间$[0.0, 0.4), [0.4, 0.8)$等。 - **条柱高度**:每个条柱高度均为$20\%$(因每个数据点独占一个分箱)。 ![示例图](https://via.placeholder.com/400x300/EFEFEF/000000?text=Percentage+Histogram+Example) (示意图:每个条柱高度为20%,纵轴标注0%至100%)
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Cyril_KI

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值