【问题标题】:Custom boolean filtering in Pandas?Pandas 中的自定义布尔过滤?
【发布时间】:2017-10-05 22:04:09
【问题描述】:

我有一个数据框

                  0         1         2         3       Marketcap
0  1.707280  0.666952  0.638515 -0.061126  2.291747     1.71B
1 -1.017134  1.353627  0.618433  0.008279  0.148128     1.82B
2 -0.774057 -0.165566 -0.083345  0.741598 -0.139851      1.1M
3 -0.630724  0.250737  1.308556 -1.040799  1.064456    30.92M
4  2.029370  0.899612  0.261146  1.474148 -1.663970   476.74k
5  2.029370  0.899612  0.261146  1.474148 -1.663970        -1

是否有某种自定义过滤方法可以让 Python 知道 B > M > K?

假设我要过滤,df[df.Marketcap > 35.00M],有没有聪明或干净的方法来做到这一点? 拥有 M 或 B 使该值非常易读且易于区分。

谢谢。

编辑:重新打开线程作为 Max U 的答案,虽然优秀似乎产生了一个熊猫错误,我们在 Github 上打开了一个问题。

【问题讨论】:

    标签: pandas filtering


    【解决方案1】:

    这不是超级干净,但它可以解决问题并且不使用任何 python 迭代:

    代码:

    # Create a separate column (which you can omit later) that converts 'Marketcap' strings to numbers
    df['cap'] = df.loc[df['Marketcap'].str.contains('B'), 'Marketcap'].str.replace('B','').astype(float) * 1000
    df['cap'].fillna(df.loc[df['Marketcap'].str.contains('M'), 'Marketcap'].str.replace('M',''), inplace = True)
    
    # For pandas pre-0.20.0 (<May 2017)
    print df.ix[df['cap'].astype(float) > 35, :-1]
    
    # For pandas 0.20.0+ (.ix[] deprecated)
    print df.iloc[df[df['cap'].astype(float) > 35].index, :-1]
    
    # Or, alternate pandas 0.20.0+ option (thanks @Psidom)
    print df[df['cap'].astype(float) > 35].iloc[:,:-1]
    

    输出:

              0         1         2         3         4 Marketcap
    0  1.707280  0.666952  0.638515 -0.061126  2.291747     1.71B
    1 -1.017134  1.353627  0.618433  0.008279  0.148128     1.82B
    4  2.029370  0.899612  0.261146  1.474148 -1.663970    100.9M
    

    【讨论】:

    • 谢谢。我刚刚意识到我的数据框中还有一个k(1000's),所以我更新了 OP 中的数据框,以反映这一点。您能否更新您的代码以反映这一点?非常感谢。
    • moondra - @MaxU 的解决方案比我的要干净得多,我认为没有必要重新发明他的轮子。
    【解决方案2】:

    来源 DF:

    In [176]: df
    Out[176]:
                        0         1         2         3 Market Cap
    0  1.707280  0.666952  0.638515 -0.061126  2.291747      1.71B
    1 -1.017134  1.353627  0.618433  0.008279  0.148128      1.82B
    2 -0.774057 -0.165566 -0.083345  0.741598 -0.139851       1.1M
    3 -0.630724  0.250737  1.308556 -1.040799  1.064456     30.92M
    4  2.029370  0.899612  0.261146  1.474148 -1.663970    476.74k
    5  2.029370  0.899612  0.261146  1.474148 -1.663970         -1
    

    解决方案:

    to_replace = ['\d+\s*[Kk]','\d+\s*[Mm]','\d+\s*[Bb]', '-1', 'N/A']
    value = [1000,1000000,1000000000, 1, 1]
    
    mask = df.assign(
        f=df['Market Cap'].replace(to_replace, value, regex=True),
        Marketcap=pd.to_numeric(df['Market Cap'].str.replace(r'[^\d\.]', ''), errors='coerce')
    ).eval("Marketcap * f < 35000000")
    
    df[mask]
    

    结果:

    In [178]: df[mask]
    Out[178]:
                        0         1         2         3 Market Cap
    2 -0.774057 -0.165566 -0.083345  0.741598 -0.139851       1.1M
    3 -0.630724  0.250737  1.308556 -1.040799  1.064456     30.92M
    4  2.029370  0.899612  0.261146  1.474148 -1.663970    476.74k
    5  2.029370  0.899612  0.261146  1.474148 -1.663970         -1
    

    PS 如果您想在结果数据集更改中保留非数字值(如 N/A):

    pd.to_numeric(df['Market Cap'].str.replace(r'[^\d\.]', ''), errors='coerce')
    

    pd.to_numeric(df['Market Cap'].str.replace(r'[^\d\.]', ''), errors='coerce').fillna('0')
    

    【讨论】:

    • 谢谢!今天晚些时候我会看看它,因为它看起来有点复杂,需要一些时间。顺便说一句,要获得这些看起来干净的输出单元格(out[178] 等),您是否通过命令行在 Ipython 中执行所有操作并仅复制单元格?我尝试复制 Jupyter 笔记本输出单元格,但是当我将其粘贴到此处时,它非常不整洁。
    • @moondra,是的,对不起,我更喜欢 iPython,因为我是个控制台人 ;-)
    • 嗨 Max,我对代码的 mask 部分有疑问; df.assign 中的第一个 f 会创建一个新列,对吗?而第二部分,Marketcap=pd.to_numeric 也正在创建一个新列?我在理解那部分时遇到了一些麻烦。谢谢!
    • @moondra,是的,f - 是一个新列(因子:1、1000、1000000 等),MarketcapMarket Cap 的干净数字表示
    【解决方案3】:

    更新:

    In [44]: df
    Out[44]:
              0         1         2         3         4 Marketcap
    0  1.707280  0.666952  0.638515 -0.061126  2.291747     1.71B
    1 -1.017134  1.353627  0.618433  0.008279  0.148128     1.82B
    2 -0.774057 -0.165566 -0.083345  0.741598 -0.139851      1.1M
    3 -0.630724  0.250737  1.308556 -1.040799  1.064456    30.92M
    4  2.029370  0.899612  0.261146  1.474148 -1.663970   476.74k
    5  2.029370  0.899612  0.261146  1.474148 -1.663970        -1
    
    In [45]: df[pd.eval(df.Marketcap.replace(['[Kk]','[Mm]','[Bb]'],
                                             ['*10**3','*10**6','*10**9'], regex=True) \
                          .add(' < 35*10**6'))]
    Out[45]:
              0         1         2         3         4 Marketcap
    2 -0.774057 -0.165566 -0.083345  0.741598 -0.139851      1.1M
    3 -0.630724  0.250737  1.308556 -1.040799  1.064456    30.92M
    4  2.029370  0.899612  0.261146  1.474148 -1.663970   476.74k
    5  2.029370  0.899612  0.261146  1.474148 -1.663970        -1
    

    我会这样做:

    In [13]: df[pd.eval(df.Marketcap.replace(['M','B'],['','*1000'], regex=True).add(' > 35'))]
    Out[13]:
              0         1         2         3         4 Marketcap
    0  1.707280  0.666952  0.638515 -0.061126  2.291747     1.71B
    1 -1.017134  1.353627  0.618433  0.008279  0.148128     1.82B
    4  2.029370  0.899612  0.261146  1.474148 -1.663970    100.9M
    

    解释:

    In [14]: df.Marketcap.replace(['M','B'],['','*1000'], regex=True)
    Out[14]:
    0    1.71*1000
    1    1.82*1000
    2          1.1
    3        30.92
    4        100.9
    Name: Marketcap, dtype: object
    
    In [15]: df.Marketcap.replace(['M','B'],['','*1000'], regex=True).add(' > 35')
    Out[15]:
    0    1.71*1000 > 35
    1    1.82*1000 > 35
    2          1.1 > 35
    3        30.92 > 35
    4        100.9 > 35
    Name: Marketcap, dtype: object
    
    In [16]: pd.eval(df.Marketcap.replace(['M','B'],['','*1000'], regex=True).add(' > 35'))
    Out[16]: array([True, True, False, False, True], dtype=object)
    

    【讨论】:

    • 为什么要启用regex = True
    • 如果我有regex = True,我会遇到这个错误; 'PandasExprVisitor' object has no attribute 'visit_Ellipsis' 。如果我将其关闭,则会遇到另一个错误,我将其作为图像发布在 OP 中。关于错误的任何想法?
    • @moondra,你的 Pandas 版本是多少?
    • 我跑了这条线,只找到了这两个; k 我负责在代码中加入“k”。我将更新 OP,以便您可以清楚地看到所有内容。
    • 好的,我把它作为一个错误提交了。希望他们能看一看。感谢您的帮助 =)
    猜你喜欢
    • 1970-01-01
    • 2018-01-31
    • 2021-02-24
    • 2019-02-05
    • 1970-01-01
    • 2019-01-26
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多