【问题标题】:Most pythonic way to concatenate pandas cells with conditions用条件连接熊猫细胞的最pythonic方法
【发布时间】:2021-09-24 10:30:18
【问题描述】:

我有以下 Pandas DataFrame,带有 city 和 arr 列:

city      arr  final_target
paris     11   paris_11
paris     12   paris_12
dallas    22   dallas
miami     15   miami
paris     16   paris_16

我的目标是填写连接 paris 和 arr 编号的 final_target 列,当城市名称为 Paris 时,当名称不是 Paris 时仅填写名称。

最pythonic的方法是什么?

【问题讨论】:

    标签: python python-3.x pandas dataframe


    【解决方案1】:

    最pythonic的方法是什么?

    这取决于定义。如果它是更可取、最常见和最快的方式,那么np.where 解决方案就是这里最 Python 的方式。


    使用numpy.where,如果需要pandaic,这个解决方案也是矢量化的,所以应该更喜欢apply (loops under the hood):

    df['final_target'] = np.where(df['city'].eq('paris'), 
                                  df['city'] + '_' + df['arr'].astype(str), 
                                  df['city'])
    

    熊猫替代品:

    df['final_target'] = df['city'].mask(df['city'].eq('paris'), 
                                         df['city'] + '_' + df['arr'].astype(str))
    

    df['final_target'] = df['city'].where(df['city'].ne('paris'), 
                                          df['city'] + '_' + df['arr'].astype(str))
    print (df)
         city  arr final_target
    0   paris   11     paris_11
    1   paris   12     paris_12
    2  dallas   22       dallas
    3   miami   15        miami
    4   paris   16     paris_16
    

    性能:

    #50k rows
    df = pd.concat([df] * 10000, ignore_index=True)
        
    
    In [157]: %%timeit
         ...: df['final_target'] = np.where(df['city'].eq('paris'), 
         ...:                               df['city'] + '_' + df['arr'].astype(str), 
         ...:                               df['city'])
         ...:                               
    48.6 ms ± 444 µs per loop (mean ± std. dev. of 7 runs, 10 loops each)
    
    In [158]: %%timeit
         ...: df['city'] + (df['city'] == 'paris')*('_' + df['arr'].astype(str))
         ...: 
         ...: 
    49.2 ms ± 1.37 ms per loop (mean ± std. dev. of 7 runs, 10 loops each)
    
    In [159]: %%timeit
         ...: df['final_target'] = df['city']
         ...: df.loc[df['city'] == 'paris', 'final_target'] +=  '_' + df['arr'].astype(str)
         ...: 
    63.8 ms ± 764 µs per loop (mean ± std. dev. of 7 runs, 10 loops each)
    
    In [160]: %%timeit
         ...: df['final_target'] = df.apply(lambda x: x.city + '_' + str(x.arr) if x.city == 'paris' else x.city, axis = 1)
         ...: 
         ...: 
    1.33 s ± 119 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)
    

    【讨论】:

    • 您能看到我的解决方案中的速度比较吗?
    • 你有解释为什么我的解决方案中的第三速度由于内存而失败而其他人没有吗?
    • @eroot163pi - 不知道,可能是面具的问题。
    【解决方案2】:

    单行代码可以解决问题:

    df['final_target'] = df.apply(lambda x: x.city + '_' + str(x.arr) if x.city == 'paris' else x.city, axis = 1)
    

    【讨论】:

    • 我认为这个解决方案不是 Python 的,因为存在矢量化更快的替代方案 - 检查 stackoverflow.com/a/54432584/2901002
    • 为什么不是pythonic?
    • 我认为因为循环在引擎盖下。我添加链接以获得更多解释为什么应该避免这个method
    • 它说apply 消耗大量内存,因为函数是逐行“应用”的。它可能比其他函数慢,但我不认为这使它“不是pythonic”......
    • 是的,我同意。这一切都在于“pythonic”的真正含义。也许如果用户正在寻找一种“快速”的方式,那么另一种方式会更合适。
    【解决方案3】:

    用loc试试这个简洁而简短的两行代码:

    df['final_target'] = df['city']
    df.loc[df['city'] == 'paris', 'final_target'] +=  '_' + df.loc[df['city'] == 'paris', 'arr'].astype(str)
    

    此解决方案首先将df['city'] 分配为final_target 列,如果city 列是paris,则添加arr 列,并用下划线分隔。

    IMO 这可能是这里最 Pythonic 和最简洁的方式。


    print(df)
    

         city  arr final_target
    0   paris   11     paris_11
    1   paris   12     paris_12
    2  dallas   22       dallas
    3   miami   15        miami
    4   paris   16     paris_16
    

    【讨论】:

    • 我认为这个需要更多内存,在大输入时失败
    • @eroot163pi 你的呢,apply 超级慢...
    • @eroot163pi 哦,对不起,我以为你是 ql.user2511...
    • 我不知道为什么会这样:P
    • df.loc 和 df.arr 它们的形状不同,但您的输出仍然是正确的,也许它可以匹配索引。仍然不清楚内存错误和采样。 stackoverflow.com/questions/69314257/…
    【解决方案4】:

    非常不言自明,一行,看起来像pythonic

    df['city'] + (df['city'] == 'paris')*('_' + df['arr'].astype(str))
    
    s = """city,arr,final_target
    paris,11,paris_11
    paris,12,paris_12
    dallas,22,dallas
    miami,15,miami
    paris,16,paris_16"""
    import pandas as pd
    import io
    df = pd.read_csv(io.StringIO(s)).sample(1000000, replace=True)
    df
    

    速度

    %%timeit
    df['city'] + (df['city'] == 'paris')*('_' + df['arr'].astype(str))
    # 877 ms ± 19.2 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)
    
    %%timeit
    df['final_target'] = np.where(df['city'].eq('paris'), 
                                  df['city'] + '_' + df['arr'].astype(str), 
                                  df['city'])
    # 874 ms ± 19.2 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)
    

    我不知道这个例子为什么会失败(更新:由于采样失败)但是内存错误仍然是个谜Why memory error when using .loc in pandas with sampling instead of direct computing

    %%timeit
    df['final_target'] = df['city']
    df.loc[df['city'] == 'paris', 'final_target'] +=  '_' + df['arr'].astype(str)
    
    MemoryError: Unable to allocate 892. GiB for an array with shape (119671145392,) and data type int64
    

    【讨论】:

    • 测试速度的行数是多少?
    • 我希望不是 5 行。
    • 大声笑...对不起,让我检查一下大的
    • 大的差别不大?
    • 是的,是一样的。
    猜你喜欢
    • 2021-08-15
    • 1970-01-01
    • 2018-12-13
    • 1970-01-01
    • 1970-01-01
    • 2017-07-12
    • 1970-01-01
    • 1970-01-01
    • 2018-12-25
    相关资源
    最近更新 更多