【问题标题】:Finding the index of the first element (e.g "True") from a series/column从系列/列中查找第一个元素的索引(例如“True”)
【发布时间】:2018-07-15 23:59:50
【问题描述】:

如何在系列或列中找到元素的索引(例如“True”)?

例如,我有一个列,我想在其中确定事件发生的第一个实例。所以我把它写成

Variable = df["Force"] < event

然后创建一个布尔系列的数据,它是假的,直到第一个实例它变成真。那么如何找到数据点的索引?

有没有更好的办法?

【问题讨论】:

    标签: python pandas indexing


    【解决方案1】:

    这是一个全熊猫解决方案,我认为它比其他一些答案更简洁。它还能够处理输入序列的值不满足条件的极端情况。

    def first_index_ordered(mask):
        assert mask.index.is_monotonic_increasing
        assert mask.dtype == bool
        idx_min = mask[mask].index.min()
        return None if pd.isna(idx_min) else idx_min
    
    col = "foo"
    thr = 42
    mask = df[col] < thr
    idx_first = first_index_ordered(mask)
    

    以上假设mask 有一个按值排序的单调递增索引。如果不是这种情况,我们必须做更多的事情:

    def first_index_unordered(mask):
        assert mask.dtype == bool
        index = mask.index
        # This creates a RangeIndex, which is monotonic
        mask = mask.reset_index(drop=True)
        idx_min = mask[mask].index.min()
        return None if pd.isna(idx_min) else index[idx_min] 
    

    当然,我们可以将这两种情况结合在一个函数中:

    def first_index_where(mask):
        if mask.index.is_monotonic_increasing:
            return first_index_ordered(mask)
        else:
            return first_index_unordered(mask)
    

    【讨论】:

      【解决方案2】:

      下面是一个我觉得很容易适应的非熊猫解决方案:

      import pandas as pd
      
      df = pd.DataFrame(dict(Force=[5, 4, 3, 2, 1]), list('abcde'))
      
      next(idx for idx, x in zip(df.index, df.Force) if x < 3)  # d
      

      它通过迭代生成器表达式的第一个结果来工作。

      相比之下,熊猫似乎表现不佳:

      df = pd.DataFrame(dict(Force=np.random.randint(0, 100000, 100000)))
      
      n = 99900
      
      %timeit df['Force'].lt(n).idxmin()
      # 1000 loops, best of 3: 1.57 ms per loop
      
      %timeit df.Force.where(df.Force > n).first_valid_index()
      # 100 loops, best of 3: 1.61 ms per loop
      
      %timeit next(idx for idx, x in zip(df.index, df.Force) if x > n)
      # 10000 loops, best of 3: 100 µs per loop
      

      【讨论】:

      • 不错的方法,具有公平的性能评估。如果想在没有元素满足条件的情况下避免StopIteration异常,只需使用带有默认值的next,例如:next(gen, None)
      【解决方案3】:

      您也可以尝试first_valid_indexwhere

      df = pd.DataFrame([[5], [4], [3], [2], [1]], columns=["Force"])
      df.Force.where(df.Force < 3).first_valid_index()
      3
      

      where 会将不满足条件的部分替换为np.nan默认。然后,我们找到该系列中的第一个有效索引。


      或者这个:选择您感兴趣的项目的一个子集,这里是Variable == 1。然后找到其索引中的第一项。

      df = pd.DataFrame([[5], [4], [3], [2], [1]], columns=["Force"])
      v = (df["Force"] < 3)
      v[v == 1].index[0]
      

      奖励:如果您需要多种物品的首次出现索引,您可以使用drop_duplicates

      df = pd.DataFrame([["yello"], ["yello"], ["blue"], ["red"],  ["blue"], ["red"]], columns=["Force"])  
      df.Force.drop_duplicates().reset_index()
          index   Force
      0   0       yello
      1   2       blue
      2   3       red
      

      还有一些工作......

      df.Force.drop_duplicates().reset_index().set_index("Force").to_dict()["index"]
      {'blue': 2, 'red': 3, 'yello': 0}
      

      【讨论】:

        【解决方案4】:

        使用idxmax 查找最大值的第一个实例。在这种情况下,True 是最大值。

        df['Force'].lt(event).idxmax()
        

        考虑样本df

        df = pd.DataFrame(dict(Force=[5, 4, 3, 2, 1]), list('abcde'))
        df
        
           Force
        a      5
        b      4
        c      3
        d      2
        e      1
        

        Force 小于3 的第一个实例位于索引'd'

        df['Force'].lt(3).idxmax()
        'd'
        

        请注意,如果 Force 的任何值都不小于 3,则最大值将为 False,第一个实例将是第一个实例。

        同时考虑替代argmax

        df.Force.lt(3).values.argmax()
        3
        

        它返回最大值的第一个实例的位置。然后你可以使用它来找到对应的index值:

        df.index[df.Force.lt(3).values.argmax()]
        'd'
        

        另外,在未来,argmax 将是一个 Series 方法。

        【讨论】:

        • 您好,谢谢。这比编写循环要容易得多。效果很好。
        猜你喜欢
        • 1970-01-01
        • 2019-05-11
        • 2019-10-10
        • 1970-01-01
        • 2013-08-22
        • 1970-01-01
        相关资源
        最近更新 更多