从系列/列中查找第一个元素的索引(例如“True”)

Posted

技术标签:

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

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

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

Variable = df["Force"] < event

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

有没有更好的办法?

【问题讨论】:

【参考方案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 方法。

【讨论】:

您好,谢谢。这比编写循环要容易得多。效果很好。

以上是关于从系列/列中查找第一个元素的索引(例如“True”)的主要内容,如果未能解决你的问题,请参考以下文章

通过从每一行的不同列中选择一个元素,从 Pandas DataFrame 创建一个系列

根据 Pandas 中另一列中的索引从列中获取数据

查找列表中第 n 个项目的索引

在不知道索引的情况下获取系列的第一个元素[重复]

从 Postgres json 中的数组返回一系列元素

查找第二次出现索引最低的第一个重复元素