【问题标题】:check if value in tuple of dataframe检查数据帧元组中的值是否
【发布时间】:2019-03-26 11:56:40
【问题描述】:

我有一个巨大的数据框(38 milj 行):

df = pd.DataFrame({'I':[1,2,3,4], 'C':[80,160,240,80],
                   'F':[(1,2,3,4),(5,7,2),(9,6,2,5,7),(4,0,8,3,2)]})

     C                F  I
0   80     (1, 2, 3, 4)  1
1  160        (5, 7, 2)  2
2  240  (9, 6, 2, 5, 7)  3
3   80  (4, 0, 8, 3, 2)  4

现在我想过滤掉'F'中包含数字3的行

给予:

     C                F  I
0   80     (1, 2, 3, 4)  1
3   80  (4, 0, 8, 3, 2)  4

有没有一种高性能、低内存使用的方法来做到这一点?

我试过np.equal((3), df['F'].values).all(),但这显然不起作用

【问题讨论】:

    标签: python pandas performance bigdata


    【解决方案1】:

    如果性能很重要,请使用 in 和 list comprehension:

    df = df[[3 in x for x in df['F']]]
    

    或者:

    df = df[df['F'].apply(set) >= set([3])]
    
    print (df)
       I   C                F
    0  1  80     (1, 2, 3, 4)
    3  4  80  (4, 0, 8, 3, 2)
    

    性能(取决于匹配值的数量,以及df 的长度):

    #[40000 rows x 3 columns]
    df = pd.concat([df] * 10000, ignore_index=True)
    
    
    In [166]: %timeit df[[3 in x for x in df['F']]]
    5.57 ms ± 132 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)
    
    In [167]: %timeit df[df['F'].apply(lambda x: 3 in x)]
    12.2 ms ± 625 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)
    
    In [170]:  %timeit df[df['F'].apply(set) >= set([3])]
    29 ms ± 396 µs per loop (mean ± std. dev. of 7 runs, 10 loops each)
    
    In [171]:  %timeit df[pd.DataFrame(df['F'].values.tolist()).eq(3).any(1)]
    37.4 ms ± 248 µs per loop (mean ± std. dev. of 7 runs, 10 loops each)
    

    更好的结构,如指向@jpp 的创建:

    from itertools import chain
    
    lens = df['F'].str.len()
    df = pd.DataFrame({
        'I' : df['I'].values.repeat(lens),
        'C' : df['C'].values.repeat(lens),
        'F' : list(chain.from_iterable(df['F'].tolist()))
    })
    print (df)
        I    C  F
    0   1   80  1
    1   1   80  2
    2   1   80  3
    3   1   80  4
    4   2  160  5
    5   2  160  7
    6   2  160  2
    7   3  240  9
    8   3  240  6
    9   3  240  2
    10  3  240  5
    11  3  240  7
    12  4   80  4
    13  4   80  0
    14  4   80  8
    15  4   80  3
    16  4   80  2
    

    【讨论】:

    • 谢谢大家,哪个最快?我猜set?
    • @Ward - 我认为最快的是 in 与列表理解。
    • @Ward - 添加了推荐的数据结构 - 将元组展平到列并重复 I 和 C 列。
    • 展开后的dataframe更像。 但是最好的解决方案取决于数组的参差不齐...例如你会在一个元组中获得 1 个值,而在另一个元组中获得 100 个值。如果你总是有 3-5 个值,我的解决方案可能会更好。如果它很破烂,jezrael 的扩展版本会更好。
    • @jpp - 是的,我同意,这取决于真实数据。但它不会创建 NaNs - 就像在您的解决方案中一样,但它会重复行。
    【解决方案2】:

    有没有一种高性能、低内存使用的方法来做到这一点?

    不,没有。 一系列元组未矢量化。它由双层指针组成,不适合 Pandas / NumPy。您可以使用诸如str 访问器或列表理解之类的技巧。或者,甚至尝试扩展为数据框:

    mask = pd.DataFrame(df['F'].values.tolist()).eq(3).any(1)
    
    print(mask)
    
    0     True
    1    False
    2    False
    3     True
    dtype: bool
    

    但所有这些都很昂贵。为了提高性能,您应该在构建系列之前改进数据的结构。

    【讨论】:

    • 我使用元组是因为我认为它会减少内存使用量。实际上它是一个 int 的列表 ('F'),它的成本 ('C') 与索引 ('I') 相关联。有没有更好的方法来构建系列以提高性能?
    • 不,tuple 确实不会减少内存存储。 NumPy 数组通过避免使用指针和使用连续内存块来减少内存存储。
    • 那么,我应该将这些 int 列表放入一个 numpy 数组,然后放入数据帧中?
    • 如果您打算执行多次计算或存储数据,可以。事实上,如果你把它分解,这就是我的解决方案所做的。对于一次性的、孤立的计算,jezrael 的解决方案更好。
    • 不,我需要对存储的数据进行多次计算。
    【解决方案3】:

    loc 中的一个简单的应用程序就可以解决问题

    df.loc[df.F.apply(lambda t : 3 in t)]
    
    
        I   C   F
    0   1   80  (1, 2, 3, 4)
    3   4   80  (4, 0, 8, 3, 2)
    

    【讨论】:

      【解决方案4】:

      您应该通过传递lambda 表达式,将in 运算符与apply 方法结合使用。

      df[df['F'].apply(lambda x: 3 in x)]
      

      输出

         I   C                F
      0  1  80     (1, 2, 3, 4)
      3  4  80  (4, 0, 8, 3, 2)
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2022-11-28
        • 1970-01-01
        • 1970-01-01
        • 2018-10-10
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多