【问题标题】:Find element's index in pandas Series在熊猫系列中查找元素的索引
【发布时间】:2013-08-22 01:34:50
【问题描述】:

我知道这是一个非常基本的问题,但由于某种原因我找不到答案。如何在 python pandas 中获取某个 Series 元素的索引? (第一次出现就足够了)

也就是说,我想要类似的东西:

import pandas as pd
myseries = pd.Series([1,4,0,7,5], index=[0,1,2,3,4])
print myseries.find(7) # should output 3

当然,可以用循环定义这样的方法:

def find(s, el):
    for i in s.index:
        if s[i] == el: 
            return i
    return None

print find(myseries, 7)

但我认为应该有更好的方法。有吗?

【问题讨论】:

    标签: python pandas


    【解决方案1】:
    >>> myseries[myseries == 7]
    3    7
    dtype: int64
    >>> myseries[myseries == 7].index[0]
    3
    

    虽然我承认应该有更好的方法来做到这一点,但这至少避免了迭代和循环对象并将其移动到 C 级别。

    【讨论】:

    • 这里的问题是它假定要搜索的元素实际上在列表中。可惜 pandas 似乎没有内置的查找操作。
    • 此解决方案仅适用于您的系列具有顺序整数索引的情况。如果你的系列索引是按日期时间的,这不起作用。
    【解决方案2】:

    转换为索引,可以使用get_loc

    In [1]: myseries = pd.Series([1,4,0,7,5], index=[0,1,2,3,4])
    
    In [3]: Index(myseries).get_loc(7)
    Out[3]: 3
    
    In [4]: Index(myseries).get_loc(10)
    KeyError: 10
    

    重复处理

    In [5]: Index([1,1,2,2,3,4]).get_loc(2)
    Out[5]: slice(2, 4, None)
    

    如果不连续返回,将返回一个布尔数组

    In [6]: Index([1,1,2,1,3,2,4]).get_loc(2)
    Out[6]: array([False, False,  True, False, False,  True, False], dtype=bool)
    

    内部使用哈希表,速度很快

    In [7]: s = Series(randint(0,10,10000))
    
    In [9]: %timeit s[s == 5]
    1000 loops, best of 3: 203 µs per loop
    
    In [12]: i = Index(s)
    
    In [13]: %timeit i.get_loc(5)
    1000 loops, best of 3: 226 µs per loop
    

    正如 Viktor 指出的那样,创建索引有一次性的创建开销(当您实际对索引执行某些操作时会产生开销,例如 is_unique

    In [2]: s = Series(randint(0,10,10000))
    
    In [3]: %timeit Index(s)
    100000 loops, best of 3: 9.6 µs per loop
    
    In [4]: %timeit Index(s).is_unique
    10000 loops, best of 3: 140 µs per loop
    

    【讨论】:

    • @Jeff 如果你有一个更有趣的索引,它不是那么容易......但我想你可以做s.index[_]
    【解决方案3】:

    另一种方法是:

    s = pd.Series([1,3,0,7,5],index=[0,1,2,3,4])
    
    list(s).index(7)
    

    返回: 3

    使用我正在使用的当前数据集进行准时测试(认为是随机的):

    [64]:    %timeit pd.Index(article_reference_df.asset_id).get_loc('100000003003614')
    10000 loops, best of 3: 60.1 µs per loop
    
    In [66]: %timeit article_reference_df.asset_id[article_reference_df.asset_id == '100000003003614'].index[0]
    1000 loops, best of 3: 255 µs per loop
    
    
    In [65]: %timeit list(article_reference_df.asset_id).index('100000003003614')
    100000 loops, best of 3: 14.5 µs per loop
    

    【讨论】:

      【解决方案4】:
      In [92]: (myseries==7).argmax()
      Out[92]: 3
      

      如果您提前知道 7 就可以使用。你可以检查这个 (myseries==7).any()

      另一种方法(与第一个答案非常相似)也解释了多个 7(或没有)是

      In [122]: myseries = pd.Series([1,7,0,7,5], index=['a','b','c','d','e'])
      In [123]: list(myseries[myseries==7].index)
      Out[123]: ['b', 'd']
      

      【讨论】:

      • 提前知道 7 是一个元素的要点是对的。然而,使用any 检查并不理想,因为需要进行两次迭代。有一个很酷的术后检查将揭示所有False 条件,你可以看到here
      • 小心,如果没有元素符合这个条件,argmax 仍然会返回 0(而不是报错)。
      【解决方案5】:

      如果你使用 numpy,你可以得到一个包含你的值的 indecies 的数组:

      import numpy as np
      import pandas as pd
      myseries = pd.Series([1,4,0,7,5], index=[0,1,2,3,4])
      np.where(myseries == 7)
      

      这将返回一个单元素元组,其中包含一个索引数组,其中 7 是 myseries 中的值:

      (array([3], dtype=int64),)
      

      【讨论】:

      • 这是我找到的最佳解决方案。
      【解决方案6】:

      你可以使用 Series.idxmax()

      >>> import pandas as pd
      >>> myseries = pd.Series([1,4,0,7,5], index=[0,1,2,3,4])
      >>> myseries.idxmax()
      3
      >>> 
      

      【讨论】:

      • 这似乎只返回找到最大元素的索引,而不是像问的问题那样特定的index of certain element
      【解决方案7】:

      您的价值通常出现在多个指标上:

      >>> myseries = pd.Series([0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 1, 1])
      >>> myseries.index[myseries == 1]
      Int64Index([3, 4, 5, 6, 10, 11], dtype='int64')
      

      【讨论】:

        【解决方案8】:

        我对这里的所有答案印象深刻。这不是一个新的答案,只是试图总结所有这些方法的时间安排。我考虑了具有 25 个元素的系列的情况,并假设索引可以包含任何值的一般情况,并且您希望索引值对应于接近系列末尾的搜索值。

        这是在 2012 Mac Mini 上使用 Python 3.9.10 和 Pandas 1.4.0 版本进行的速度测试。

        In [1]: import pandas as pd
        
        In [2]: import numpy as np
        
        In [3]: data = [406400, 203200, 101600, 76100, 50800, 25400, 19050, 12700, 950
           ...: 0, 6700, 4750, 3350, 2360, 1700, 1180, 850, 600, 425, 300, 212, 150, 1
           ...: 06, 75, 53, 38]
        
        In [4]: myseries = pd.Series(data, index=range(1,26))
        
        In [5]: assert(myseries[21] == 150)
        
        In [6]: %timeit myseries[myseries == 150].index[0]
        179 µs ± 891 ns per loop (mean ± std. dev. of 7 runs, 10000 loops each)
        
        In [7]: %timeit myseries[myseries == 150].first_valid_index()
        205 µs ± 3.67 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)
        
        In [8]: %timeit myseries.where(myseries == 150).first_valid_index()
        597 µs ± 4.03 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)
        
        In [9]: %timeit myseries.index[np.where(myseries == 150)[0][0]]
        110 µs ± 872 ns per loop (mean ± std. dev. of 7 runs, 10000 loops each)
        
        In [10]: %timeit pd.Series(myseries.index, index=myseries)[150]
        125 µs ± 2.56 µs per loop (mean ± std. dev. of 7 runs, 10000 loops each)
        
        In [11]: %timeit myseries.index[pd.Index(myseries).get_loc(150)]
        49.5 µs ± 814 ns per loop (mean ± std. dev. of 7 runs, 10000 loops each)
        
        In [12]: %timeit myseries.index[list(myseries).index(150)]
        7.75 µs ± 36.1 ns per loop (mean ± std. dev. of 7 runs, 100000 loops each)
        
        In [13]: %timeit myseries.index[myseries.tolist().index(150)]
        2.55 µs ± 27.3 ns per loop (mean ± std. dev. of 7 runs, 100000 loops each)
        
        In [14]: %timeit dict(zip(myseries.values, myseries.index))[150]
        9.89 µs ± 79.7 ns per loop (mean ± std. dev. of 7 runs, 100000 loops each)
        
        In [15]: %timeit {v: k for k, v in myseries.items()}[150]
        9.99 µs ± 67 ns per loop (mean ± std. dev. of 7 runs, 100000 loops each)
        
        

        @Jeff 的回答似乎是最快的 - 尽管它不处理重复。

        更正:对不起,我错过了一个,@Alex Spangher 使用列表索引方法的解决方案是迄今为止最快的。

        更新:添加了@EliadL 的答案。

        希望这会有所帮助。

        令人惊讶的是,如此简单的操作需要如此复杂的解决方案,而且许多解决方案如此缓慢。在某些情况下超过半毫秒才能找到一系列 25 中的值。

        2022-02-18 更新

        使用最新的 Pandas 版本和 Python 3.9 更新了所有计时。即使在较旧的计算机上,与之前的测试(版本 0.25.3)相比,所有时间都显着减少(10% 到 70%)。

        另外:增加了两个使用字典的方法。

        【讨论】:

        • 谢谢。但是您不应该在 myindex 创建之后进行测量,因为它只需要创建一次吗?
        • 您可能会争辩说,但这取决于需要多少次这样的查找。如果您要进行多次查找,则只值得创建myindex 系列。对于这个测试,我假设它只需要一次,并且总执行时间很重要。
        • 今晚刚刚遇到了这个需求,并且在多个查找中对同一个索引对象使用 .get_lock() 似乎应该是最快的。我认为对答案的改进是为两者提供时间:包括索引创建,以及另一个仅在创建后查找的时间。
        • 是的,好点。 @EliadL 也这么说。这取决于该系列有多少应用程序是静态的。如果系列中的任何值发生变化,您需要重建pd.Index(myseries)。为了公平起见,我假设原始系列可能自上次查找以来发生了变化。
        【解决方案9】:

        另一种尚未提及的方法是 tolist 方法:

        myseries.tolist().index(7)
        

        应该返回正确的索引,假设值存在于系列中。

        【讨论】:

        • @Alex Spangher 在 2014 年 9 月 17 日提出了类似的建议。看他的回答。我现在已经将两个版本都添加到了测试结果中。
        【解决方案10】:

        这是我能找到的最原生且可扩展的方法:

        >>> myindex = pd.Series(myseries.index, index=myseries)
        
        >>> myindex[7]
        3
        
        >>> myindex[[7, 5, 7]]
        7    3
        5    4
        7    3
        dtype: int64
        

        【讨论】:

          【解决方案11】:

          Pandas 有一个内置类 Index 和一个名为 get_loc 的函数。这个函数要么返回

          index(元素索引)
          切片(如果指定的数字是按顺序排列的)
          数组(如果数字在多个索引处,则为布尔数组)

          例子:

          import pandas as pd
          
          >>> mySer = pd.Series([1, 3, 8, 10, 13])
          >>> pd.Index(mySer).get_loc(10)  # Returns index
          3  # Index of 10 in series
          
          >>> mySer = pd.Series([1, 3, 8, 10, 10, 10, 13])
          >>> pd.Index(mySer).get_loc(10)  # Returns slice
          slice(3, 6, None)  # 10 occurs at index 3 (included) to 6 (not included)
          
          
          # If the data is not in sequence then it would return an array of bool's.
          >>> mySer = pd.Series([1, 10, 3, 8, 10, 10, 10, 13, 10])
          >>> pd.Index(mySer).get_loc(10)
          array([False, True, False, False, True, True, False, True])
          
          

          还有很多其他选择,但我发现它对我来说非常简单。

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 2018-12-29
            • 2020-02-19
            • 2017-09-17
            • 2018-10-13
            • 1970-01-01
            相关资源
            最近更新 更多