使用 `pandas.cut()` 函数,如何获取整数区间并避免得到负数的最低边界?

17

我的数据框中最小值为零。我试图使用 pandas.cut()precisioninclude_lowest 参数,但是我无法得到由整数组成而不是浮点数的间隔,并且我也无法使最左侧的间隔停在零。

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值会扩展0.1%的x范围,但我认为也许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')

enter image description here

是否有一种方法可以直接使用 pandas.cut() 获得非负整数作为间隔边界,而不使用numpy?

编辑:我刚注意到指定 right=False 会使最低区间从-0.4移动到0。看来它优先于 include_lowest,因为改变后者与 right=False 的组合没有任何可见的效果。下面的区间仍然用一个小数点指定。

enter image description here


解决此问题的建议:https://github.com/pandas-dev/pandas/issues/47996 - Florin Andrei
3个回答

5

您应该明确设置标签参数

准备工作:

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)

1
如果(higher-lower)/n_bins不是整数,这个会起作用吗? - user4446237

3

其他答案(包括 OP 的 np.histogram 解决方法)似乎不再起作用。它们有赞成票,所以我不确定这些年是否发生了什么变化。

IntervalIndex 要求所有的区间都要封闭,因此 [0, 53] 无法与 (322, 376] 共存。


以下是两种基于重新标记方法的可行解决方案:

  1. Without numpy, reuse pd.cut edges as pd.cut labels

    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]
    
  2. With numpy, convert np.linspace edges into pd.cut labels

    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')


3

@joelostblom,你已经完成了大部分工作,不需要使用numpy,而是直接使用pandas提供的返回bins的函数。

_, 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)

最简单和最Pythonic的。 - CodeTrek

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接