【问题标题】:Efficient way to get the unique values from 2 or more columns in a Dataframe从数据框中的 2 列或更多列中获取唯一值的有效方法
【发布时间】:2016-12-08 14:12:55
【问题描述】:

给定来自SFrame 的矩阵:

>>> from sframe import SFrame
>>> sf =SFrame({'x':[1,1,2,5,7], 'y':[2,4,6,8,2], 'z':[2,5,8,6,2]})
>>> sf
Columns:
    x   int
    y   int
    z   int

Rows: 5

Data:
+---+---+---+
| x | y | z |
+---+---+---+
| 1 | 2 | 2 |
| 1 | 4 | 5 |
| 2 | 6 | 8 |
| 5 | 8 | 6 |
| 7 | 2 | 2 |
+---+---+---+
[5 rows x 3 columns]

我想获取 xy 列的唯一值,我可以这样做:

>>> sf['x'].unique().append(sf['y'].unique()).unique()
dtype: int
Rows: 7
[2, 8, 5, 4, 1, 7, 6]

这样我得到 x 的唯一值和 y 的唯一值,然后附加它们并获取附加列表的唯一值。

我也可以这样做:

>>> sf['x'].append(sf['y']).unique()
dtype: int
Rows: 7
[2, 8, 5, 4, 1, 7, 6]

但是这样一来,如果我的 x 和 y 列很大并且有很多重复项,我会在获得唯一性之前将其附加到一个非常大的容器中。

是否有更有效的方法来获取从 SFrame 中的 2 个或更多列创建的组合列的唯一值?

pandas 中的 2 列或更多列中获取唯一值的有效方法在 pandas 中的等效性是什么?

【问题讨论】:

  • 输出中元素的顺序是否重要?将输出作为列表还是数组可以吗?
  • SFrame's API 与 Pandas API 相比相当差,因此与您的解决方案相比,我认为您无法以更有效的方式做到这一点。也许是时候考虑使用Apache Spark了?

标签: python csv pandas dataframe sframe


【解决方案1】:

我没有 SFrame 但在 pd.DataFrame 上测试过:

  sf[["x", "y"]].stack().value_counts().index.tolist()
  [2, 1, 8, 7, 6, 5, 4]

【讨论】:

  • 快速提问,在这种情况下,在执行.value_counts() 之前,将一列(重复)附加到另一列(重复)对吗?
  • 是的,代码堆叠数据,以便访问基础值。
  • 它有效,我赞成。但我认为 sframe 答案会更合适。希望你不介意=)
  • 我相信.unique()value_counts() 更有效率。
【解决方案2】:

我能想到的最简单的方法是转换为 numpy 数组,然后找到唯一值

np.unique(sf[['x', 'y']].to_numpy())

array([1, 2, 4, 5, 6, 7, 8])

如果您需要在 sframe 中使用它

SFrame({'xy_unique': np.unique(sf[['x', 'y']].to_numpy())})

【讨论】:

    【解决方案3】:

    SFrame

    我没有使用过 SFrame,也不知道它在什么条件下复制数据。 (选择sf['x']append 是否将数据复制到内存?)。 SFrame 中有 pack_columnsstack 方法,如果它们不复制数据,那么这应该可以:

    sf[['x', 'y']].pack_columns(new_column_name='N').stack('N').unique()
    

    熊猫

    如果您的数据适合内存,那么您可以在 pandas 中高效地执行此操作,而无需额外的副本。

    # copies the data to memory
    df = sf[['x', 'y']].to_dataframe()
    
    # a reference to the underlying numpy array (no copy)
    vals = df.values
    
    # 1d array: 
    # (numpy.ravel doesn't copy if it doesn't have to - it depends on the data layout)
    if np.isfortran(vals):
        vals_1d = vals.ravel(order='F')
    else:
        vals_1d = vals.ravel(order='C')
    
    uniques = pd.unique(vals_1d)
    

    pandas 的 unique 比 numpy 的 np.unique 更高效,因为它不排序。

    【讨论】:

      【解决方案4】:

      查看this answer 的类似问题。请注意,Pandas 的 pd.unique 函数比 Numpy 的要快得多。

      >>> pd.unique(sf[['x','y']].values.ravel())
      array([2, 8, 5, 4, 1, 7, 6], dtype=object)
      

      【讨论】:

        【解决方案5】:

        虽然我不知道如何在 SFrame 中做到这一点,但这里对@Merlin 的回答进行了更长的解释:

        >>> import pandas as pd
        >>> df = pd.DataFrame({'x':[1,1,2,5,7], 'y':[2,4,6,8,2], 'z':[2,5,8,6,2]})
        >>> df[['x', 'y']]
           x  y
        0  1  2
        1  1  4
        2  2  6
        3  5  8
        4  7  2
        

        仅提取列 X 和 Y

        >>> df[['x', 'y']] # Extract only columns x and y
           x  y
        0  1  2
        1  1  4
        2  2  6
        3  5  8
        4  7  2
        

        将每行的 2 列堆叠成 1 列行,同时仍然能够将它们作为字典进行访问:

        >>> df[['x', 'y']].stack()                       
        0  x    1
           y    2
        1  x    1
           y    4
        2  x    2
           y    6
        3  x    5
           y    8
        4  x    7
           y    2
        dtype: int64
        >>> df[['x', 'y']].stack()[0]      
        x    1
        y    2
        dtype: int64
        >>> df[['x', 'y']].stack()[0]['x']
        1
        >>> df[['x', 'y']].stack()[0]['y']
        2
        

        计算组合列中所有元素的单个值:

        >>> df[['x', 'y']].stack().value_counts() # index(i.e. keys)=elements, Value=counts
        2    3
        1    2
        8    1
        7    1
        6    1
        5    1
        4    1
        

        访问索引和计数:

        >>> df[['x', 'y']].stack().value_counts().index      
        Int64Index([2, 1, 8, 7, 6, 5, 4], dtype='int64')
        >>> df[['x', 'y']].stack().value_counts().values  
        array([3, 2, 1, 1, 1, 1, 1])
        

        转换为列表:

        >>> sf[["x", "y"]].stack().value_counts().index.tolist()
        [2, 1, 8, 7, 6, 5, 4]
        

        仍然是一个 SFrame 答案也会很棒。相同的语法不适用于 SFrame。

        【讨论】:

        • IIUC, stack() 很可能会复制基础数据——这可能是您想要避免的。
        【解决方案6】:

        以下是三种可能方法之间的一个小基准:

        from sframe import SFrame
        import numpy as np
        import pandas as pd
        import timeit
        
        sf = SFrame({'x': [1, 1, 2, 5, 7], 'y': [2, 4, 6, 8, 2], 'z': [2, 5, 8, 6, 2]})
        
        
        def f1(sf):
            return sf['x'].unique().append(sf['y'].unique()).unique()
        
        
        def f2(sf):
            return sf['x'].append(sf['y']).unique()
        
        
        def f3(sf):
            return np.unique(sf[['x', 'y']].to_numpy())
        
        N = 1000
        
        print timeit.timeit('f1(sf)', setup='from __main__ import f1, sf', number=N)
        print timeit.timeit('f2(sf)', setup='from __main__ import f2, sf', number=N)
        print timeit.timeit('f3(sf)', setup='from __main__ import f3, sf', number=N)
        
        # 13.3195129933
        # 4.66225642657
        # 3.65669089489
        # [Finished in 23.6s]
        

        在 windows7+i7_2.6ghz 上使用 python2.7.11 x64 进行基准测试

        结论:我建议你使用np.unique,基本上是f3

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 2020-07-11
          • 2023-02-07
          • 2016-07-02
          • 2022-01-02
          • 2011-10-26
          • 2018-06-16
          相关资源
          最近更新 更多