【问题标题】:How to filter values using Pandas?如何使用 Pandas 过滤值?
【发布时间】:2020-06-26 23:21:35
【问题描述】:

意图:使用 pandas 根据汉明权重过滤二进制数。在这里,我检查二进制中出现的 1 的数量并将计数写入 df。

目前的努力:

import pandas as pd
def ones(num):
    return bin(num).count('1')
num = list(range(1,8))
C = pd.Index(["num"])
df = pd.DataFrame(num, columns=C)
df['count'] = df.apply(lambda row : ones(row['num']), axis = 1)
print(df) 

输出:

   num  count
0    1      1
1    2      1
2    3      2
3    4      1
4    5      2
5    6      2
6    7      3


Intended output:
  1 2 3
0 1 3 7
1 2 5
2 4 6

救命!

【问题讨论】:

    标签: python pandas numpy


    【解决方案1】:

    您可以使用pivot_table。尽管您需要将index 定义为分组count 列的cumcount,但pivot_table 无法自行解决所有问题:)

    (df.pivot_table(index=df.groupby('count').cumcount(), 
                    columns='count', 
                    values='num'))
    
    count    1    2    3
    0      1.0  3.0  7.0
    1      2.0  5.0  NaN
    2      4.0  6.0  NaN
    

    你也有参数fill_value,虽然我不建议你使用它,因为你会得到混合类型。现在看起来NumPy 将是一个不错的选择,您可以使用new_df.to_numpy() 从结果中轻松获取数组。


    另外,关注ones 中的逻辑,我们可以将其向量化(基于this answer):

    m = df.num.to_numpy().itemsize
    df['count'] = (df.num.to_numpy()[:,None] & (1 << np.arange(m)) > 0).view('i1').sum(1)
    

    以下是对这两种方法的性能的检查:

    df_large = pd.DataFrame({'num':np.random.randint(0,10,(10_000))})
    
    def vect(df):
        m = df.num.to_numpy().itemsize
        (df.num.to_numpy()[:,None] & (1 << np.arange(m)) > 0).view('i1').sum(1)
    
    %timeit vect(df_large)
    # 340 µs ± 5.6 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)
    
    %timeit df_large.apply(lambda row : ones(row['num']), axis = 1)
    # 103 ms ± 2.32 ms per loop (mean ± std. dev. of 7 runs, 10 loops each)
    

    【讨论】:

      【解决方案2】:

      我建议一个不同的输出:

      df.groupby("count").agg(list)
      

      这会给你

                   num
      count           
      1      [1, 2, 4]
      2      [3, 5, 6]
      3            [7]
      

      相同的信息格式略有不同。在您原来的旋转格式中,行是没有意义的,并且您有未确定的列数。我建议更常见的是行数不确定。我想你会发现这更容易继续工作。

      或者考虑只创建一个字典作为 DataFrame 在这里增加了很多开销而没有任何好处:

      df.groupby("count").agg(list).to_dict()["num"]
      

      给你

      {
          1: [1, 2, 4], 
          2: [3, 5, 6], 
          3: [7],
      }
      

      【讨论】:

      • OP 希望在二进制表示中将具有相同数量的1s 的数字分组。我不认为枢轴是输出的最佳数据结构,这是他们可能没有想到的替代方案
      • 如果可以避免,列表的 df 绝不是一个好主意。即使使用最简单的操作,性能也会下降一个大型数据帧
      • tbh 它应该是一本字典。无论哪种方式,df 都没有多大意义。
      【解决方案3】:

      这是一种方法

      df.groupby('count')['num'].agg(list).apply(pd.Series).T
      

      【讨论】:

        猜你喜欢
        • 2016-05-11
        • 2019-02-24
        • 2019-06-04
        • 2022-07-05
        • 1970-01-01
        • 2021-12-27
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多