我的数据框的最低值为零。我正在尝试使用
precision
的 include_lowest
和 pandas.cut()
参数,但我无法获得由整数组成的间隔,而不是带有一位小数的浮点数。我也无法让最左边的间隔停在零。
import pandas as pd
import seaborn as sns
import matplotlib.pyplot as plt
sns.set(style='white', font_scale=1.3)
df = pd.DataFrame(range(0,389,8)[:-1], columns=['value'])
df['binned_df_pd'] = pd.cut(df.value, bins=7, precision=0, include_lowest=True)
sns.pointplot(x='binned_df_pd', y='value', data=df)
plt.xticks(rotation=30, ha='right')
我尝试将
precision
设置为-1、0和1,但它们都输出一位十进制浮点数。 pandas.cut()
帮助确实提到 x-min 和 x-max 值扩展了 x-范围的 0.1%,但我认为 include_lowest
可能会以某种方式抑制这种行为。我当前的解决方法包括导入 numpy:
import numpy as np
bin_counts, edges = np.histogram(df.value, bins=7)
edges = [int(x) for x in edges]
df['binned_df_np'] = pd.cut(df.value, bins=edges, include_lowest=True)
sns.pointplot(x='binned_df_np', y='value', data=df)
plt.xticks(rotation=30, ha='right')
有没有办法直接用
pandas.cut()
获取非负整数作为区间边界而不使用numpy?
编辑:我刚刚注意到指定
right=False
会使最低间隔移动到0而不是-0.4。它似乎优先于 include_lowest
,因为更改后者与 right=False
结合使用不会产生任何明显的效果。以下间隔仍指定一位小数。
您应该专门设置
labels
参数
lower, higher = df['value'].min(), df['value'].max()
n_bins = 7
edges = range(lower, higher, (higher - lower)/n_bins) # the number of edges is 8
lbs = ['(%d, %d]'%(edges[i], edges[i+1]) for i in range(len(edges)-1)]
df['binned_df_pd'] = pd.cut(df.value, bins=n_bins, labels=lbs, include_lowest=True)
其他答案(包括OP的
np.histogram
解决方法)似乎都不再有效。他们有投票,所以我不确定这些年来是否发生了变化。
IntervalIndex
要求所有区间都同样闭合,因此 [0, 53]
不能与 (322, 376]
共存。
以下是基于重新标记方法的两种可行解决方案:
没有numpy,重用
pd.cut
边缘作为pd.cut
标签
bins = 7
_, edges = pd.cut(df.value, bins=bins, retbins=True)
labels = [f'({abs(edges[i]):.0f}, {edges[i+1]:.0f}]' for i in range(bins)]
df['bin'] = pd.cut(df.value, bins=bins, labels=labels)
# value bin
# 1 8 (0, 53]
# 2 16 (0, 53]
# .. ... ...
# 45 360 (322, 376]
# 46 368 (322, 376]
使用 numpy, 将
np.linspace
边转换为 pd.cut
标签
bins = 7
edges = np.linspace(df.value.min(), df.value.max(), bins+1).astype(int)
labels = [f'({edges[i]}, {edges[i+1]}]' for i in range(bins)]
df['bin'] = pd.cut(df.value, bins=bins, labels=labels)
# value bin
# 1 8 (0, 53]
# 2 16 (0, 53]
# .. ... ...
# 45 360 (322, 376]
# 46 368 (322, 376]
注意:仅更改标签,因此基础分箱仍将以 0.1% 的边距发生。
pointplot()
输出(从pandas 1.2.4开始):
sns.pointplot(x='bin', y='value', data=df)
plt.xticks(rotation=30, ha='right')
@joelostblom,你已经完成了大部分工作,而不是使用 numpy, 只需使用 pandas 已经提供的功能,即返回垃圾箱。
_, edges = pd.cut(df.value, bins=7, retbins=True)
edges = [int(x) for x in edges]
df['binned_df_np'] = pd.cut(df.value, bins=edges, include_lowest=True)
您也可以拥有闭整数区间。让
nbins = 7
。
找到要切割的边缘(Pandas 或 Numpy)。
# NumPy
edges = np.linspace(df.value.min(), df.value.max(), nbins + 1)
edges[-1] += 1
# Pandas
float_binned, edges = pd.cut(df.value, bins=nbins, right=False, retbins=True)
edges[-1] = df.values.max() + 1
对于您的数据,这是:
[ 0. , 53.71, 107.43, 161.14, 214.86, 268.57, 322.29, 377. ]
从边缘开始形成闭合整数区间。
edges = edges.round() # optional, for more uniform length of intervals
intervals = [pd.Interval(int(left), int(right) - 1, 'both')
for left, right in zip(edges[:-1], edges[1:])]
对于您的数据,这是:
[[0, 53], [54, 106], [107, 160], [161, 214], [215, 268], [269, 321], [322, 376]]
使用间隔切割数据。
int_binned = pd.cut(df.value, intervals)
对于您的数据,这是:
0 [0, 53]
1 [0, 53]
2 [0, 53]
...
45 [322, 376]
46 [322, 376]
47 [322, 376]
Name: value, dtype: category
Categories (7, interval[int64, both]): [[0, 53] < [54, 106] < [107, 160] < [161, 214] < [215, 268] < [269, 321] < [322, 376]]
然后你就可以制作你的情节了:
df['binned_value'] = int_binned
sns.pointplot(x='binned_value', y='value', data=df)
plt.xticks(rotation=30, ha='right')