【问题标题】:Why use pandas qcut return ValueError: Bin edges must be unique?为什么使用 pandas qcut return ValueError: Bin 边缘必须是唯一的?
【发布时间】:2016-08-21 04:48:27
【问题描述】:

我有数据集:

recency;frequency;monetary
21;156;41879955
13;88;16850284
8;74;79150488
2;74;26733719
9;55;16162365
...;...;...

详细原始数据 -> http://pastebin.com/beiEeS80 我输入DataFrame,这是我的完整代码:

df = pd.DataFrame(datas, columns=['userid', 'recency', 'frequency', 'monetary'])
df['recency'] = df['recency'].astype(float)
df['frequency'] = df['frequency'].astype(float)
df['monetary'] = df['monetary'].astype(float)

df['recency'] = pd.qcut(df['recency'].values, 5).codes + 1
df['frequency'] = pd.qcut(df['frequency'].values, 5).codes + 1
df['monetary'] = pd.qcut(df['monetary'].values, 5).codes + 1

但是返回错误

df['frequency'] = pd.qcut(df['frequency'].values, 5).codes + 1
ValueError: Bin edges must be unique: array([   1.,    1.,    2.,    4.,    9.,  156.])

如何解决?

【问题讨论】:

    标签: python pandas


    【解决方案1】:

    我在 Jupyter 中运行了这个并将 exampledata.txt 放在与笔记本相同的目录中。

    请注意第一行:

    df = pd.DataFrame(datas, columns=['userid', 'recency', 'frequency', 'monetary'])
    

    当未在数据文件中定义时加载列 'userid'。我删除了这个列名。

    解决方案

    import pandas as pd
    
    def pct_rank_qcut(series, n):
        edges = pd.Series([float(i) / n for i in range(n + 1)])
        f = lambda x: (edges >= x).argmax()
        return series.rank(pct=1).apply(f)
    
    datas = pd.read_csv('./exampledata.txt', delimiter=';')
    
    df = pd.DataFrame(datas, columns=['recency', 'frequency', 'monetary'])
    
    df['recency'] = df['recency'].astype(float)
    df['frequency'] = df['frequency'].astype(float)
    df['monetary'] = df['monetary'].astype(float)
    
    df['recency'] = pct_rank_qcut(df.recency, 5)
    df['frequency'] = pct_rank_qcut(df.frequency, 5)
    df['monetary'] = pct_rank_qcut(df.monetary, 5)
    

    说明

    您看到的问题是 pd.qcut 假设 5 个大小相同的 bin 的结果。在您提供的数据中,'frequency' 有超过 28% 的数字 1。这打破了qcut

    我提供了一个新函数 pct_rank_qcut 来解决这个问题并将所有 1 推入第一个 bin。

        edges = pd.Series([float(i) / n for i in range(n + 1)])
    

    此行根据n 定义的所需箱数定义了一系列百分位边缘。在n = 5 的情况下,边缘将为[0.0, 0.2, 0.4, 0.6, 0.8, 1.0]

        f = lambda x: (edges >= x).argmax()
    

    这一行定义了一个辅助函数以应用于下一行的另一个系列。 edges >= x 将返回一个长度等于edges 的系列,其中每个元素是TrueFalse,具体取决于x 是否小于或等于该边。对于x = 0.14,生成的(edges >= x) 将是[False, True, True, True, True, True]。通过采用argmax(),我确定了系列为True 的第一个索引,在本例中为1

        return series.rank(pct=1).apply(f)
    

    这一行接受输入series 并将其转换为百分位排名。我可以将这些排名与我创建的边缘进行比较,这就是我使用apply(f) 的原因。返回的应该是一系列编号为 1 到 n 的 bin 编号。这一系列 bin 编号与您尝试使用的相同:

    pd.qcut(df['recency'].values, 5).codes + 1
    

    这会导致 bin 不再相等,并且 bin 1 完全从 bin 2 借用。但必须做出一些选择。如果您不喜欢这个选择,请使用这个概念来建立您自己的排名。

    演示

    print df.head()
    
       recency  frequency  monetary
    0        3          5         5
    1        2          5         5
    2        2          5         5
    3        1          5         5
    4        2          5         5
    

    更新

    pd.Series.argmax() 现已弃用。只需切换到pd.Series.values.argmax()() 即可更新!

    def pct_rank_qcut(series, n):
        edges = pd.Series([float(i) / n for i in range(n + 1)])
        f = lambda x: (edges >= x).values.argmax()
        return series.rank(pct=1).apply(f)
    

    【讨论】:

    • 谢谢你的回答,如果我错了,请纠正,你的解决方案不是改变价值吗?
    • @ihsansat 我不确定你在问什么。您是在问我的解决方案是否改变了任何值?我创建了一个 DataFrame 并随后更改了该 DataFrame 中的值。你问的是这个吗?
    • 此解决方案应该与您对“新近”和“货币”的解决方案相同。但是您没有任何“频率”,因为数据的分布不允许相同大小的桶的干净 qcut。我应用了我认为是好的逻辑来得到有意义的东西。这是我在类似情况下会做出的几个选择之一。但是,您绝对应该自己检查结果并确保您对它感到满意。
    • 对不起@piRTSquared,我还是不明白,pct_rank_qcut 到底是做什么的?
    【解决方案2】:

    讨论了各种解决方案here,但简要:

    如果您使用的是 pandas,>= 0.20.0 他们添加了一个选项 duplicates='raise'|'drop' 来控制是在重复的边缘上升高还是丢弃它们,这将导致 bin 少于指定的数量,并且一些比其他更大(包含更多元素)。

    对于以前的 pandas 版本,请尝试传递排名值而不是值本身:

    pd.qcut(df['frequency'].rank(method='first').values, 5).codes + 1
    

    通过这种方式,您可能会将相同的值放入不同的分位数。这可能正确或不正确,具体取决于您的具体需求(如果这不是您想要的,您可能想看看 pandas.cut ,它根据值本身选择要均匀分布的箱,而 pandas.qcut 选择箱,以便您在每个箱中拥有相同数量的记录)

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2016-11-13
      • 1970-01-01
      • 2017-11-22
      • 2013-12-08
      • 1970-01-01
      • 2018-07-26
      • 2018-01-07
      • 1970-01-01
      相关资源
      最近更新 更多