【问题标题】:how low in dataframe to find all elements of list找到列表的所有元素的数据帧有多低
【发布时间】:2021-10-27 00:26:59
【问题描述】:

我有一个清单:

elements = ['a', 'b', 'c', 'd']

还有一个包含我列表中部分或全部元素的数据框:

       mycol
0      a
1      x
2      y
3      e
4      b
5      c
6      o
7      l
8      s
9      d
10     g

我想知道我必须在我的 df 上搜索多低才能找到我列表中的所有元素。在这种情况下,答案将是 10,因为直到我找到列表中的所有元素为止。

谢谢

【问题讨论】:

  • 这听起来不太可能有内置函数。只需遍历数据帧索引。如果当前 df 元素在列表中,则将其从列表中删除。当列表为空时,当前索引就是答案。

标签: python pandas list dataframe


【解决方案1】:

试试idxmax:

>>> df['mycol'].isin(elements)[::-1].idxmax()
9
>>> 

编辑:

要指定元素中的所有值都在数据框中,请尝试:

x = df['mycol'].drop_duplicates().isin(elements).cumsum().eq(len(elements))
if x.any():
    print(x.idxmax())
else:
    print("Not all values are in the dataframe")

对于您当前的数据框:

9

对于并非所有值都在数据框中的数据框:

Not all values are in the dataframe

【讨论】:

  • 这不就是在elements中找到最后一个匹配的元素值吗?似乎没有任何关于查找“列表的所有元素”的逻辑。尽管我们只看到了d,但DataFrame df = pd.DataFrame({'mycol': ['z', 'z', 'z', 'z', 'z', 'z', 'z', 'z', 'z', 'd', 'g']}) 也会产生9
  • @HenryEcker 编辑了我的答案,请查看
  • @HenryEcker 编辑了我的答案。
  • @HenryEcker Edietd 再次
  • 这不适用于一些更大的数据,例如df = pd.DataFrame({'mycol': np.random.choice(list(string.ascii_lowercase), size=100000)}); elements = list(np.random.choice(list(string.ascii_lowercase), size=100))
【解决方案2】:

我们可以使用np.uniquereturn_index=True 来查找每个唯一值的第一个实例:

import numpy as np
import pandas as pd

elements = ['a', 'b', 'c', 'd']
df = pd.DataFrame({
    'mycol': ['a', 'x', 'y', 'e', 'b', 'c', 'o', 'l', 's', 'd', 'g']
})

# Find the first location where each unique value is found
a, b = np.unique(df['mycol'], return_index=True)
# Compare unique values to values we're looking for
m = (a == np.array(elements)[:, None])
# If we have a location for all elements
if m.any(axis=1).all():
    # Find the highest index value
    max_index = b[m.any(axis=0)].max()
    # Offset index by one to match expected output
    print('All values found by', max_index + 1)
else:
    # We couldn't find all elements
    print('Not all elements found.')
All values found by 10

混合顺序和重复的示例:

elements = ['a', 'b', 'c', 'd']
df = pd.DataFrame({
    'mycol': ['d', 'x', 'c', 'a', 'b', 'c', 'o', 'd', 's', 'd', 'g']
})
   mycol
0      d
1      x
2      c
3      a
4      b
5      c
6      o
7      d
8      s
9      d
10     g
All values found by 5

未找到所有元素的示例:

elements = ['a', 'b', 'c', 'z']
df = pd.DataFrame({
    'mycol': ['d', 'x', 'c', 'a', 'b', 'c', 'o', 'd', 's', 'd', 'g']
})
   mycol
0      d
1      x
2      c
3      a
4      b
5      c
6      o
7      d
8      s
9      d
10     g
Not all elements found.  # (No z)

【讨论】:

    【解决方案3】:

    值得考虑Barmar's comment。我无法获得更好的索引答案来处理一些更大的测试数据,但 Barmar 的循环应该是可靠的:

    只需遍历数据框索引。如果当前 df 元素在列表中,则将其从列表中删除。当列表为空时,当前索引就是答案。

    def idxall(series, elements):
        for i, e in enumerate(series.to_numpy()): # faster than series.items()
            if e in elements:
                elements.remove(e)
                if not elements:
                    return i + 1
        return np.nan
    

    时间

    给定df = pd.DataFrame({'mycol': np.random.choice(list(string.ascii_lowercase), size=1000)})

    %timeit tdy_idxall(df.mycol, list(string.ascii_lowercase))
    # 21.4 µs ± 7.44 µs per loop (mean ± std. dev. of 7 runs, 10000 loops each)
    
    %timeit henry_ecker_np_unique(df.mycol, list(string.ascii_lowercase))
    # 379 µs ± 48.1 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)
    
    %timeit u12_forward_idxmax(df.mycol, list(string.ascii_lowercase)
    # 538 µs ± 61.7 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)
    
    %timeit corralien_idxall(df.mycol, list(string.ascii_lowercase))
    # 1.28 ms ± 243 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)
    

    验证

    • 使用 OP 的示例:

      df = pd.DataFrame({'mycol': list('axyebcolsdg')})
      elements = list('abcd')
      
      idxall(df.mycol, elements)
      # 10
      
    • 使用 Henry 的示例 #1(混合顺序和重复):

      df = pd.DataFrame({'mycol': list('dxcabcodsdg')})
      elements = list('abcd')
      
      idxall(df.mycol, elements)
      # 5
      
    • 使用 Henry 的示例 #2(未找到所有元素):

      df = pd.DataFrame({'mycol': list('dxcabcodsdg')})
      elements = list('abcz')
      
      idxall(df.mycol, elements)
      # nan
      

    【讨论】:

      【解决方案4】:

      您可以使用pd.CategoricalDtypeset 检查是否所有元素都在过滤后的数据框中:

      def idxall(series, elements):
          out = series.astype(pd.CategoricalDtype(elements)) \
                      .reset_index(drop=True) \
                      .dropna().drop_duplicates()
          return out.index.max()+1 if not set(elements).difference(out) else np.nan
      
      1. 您的样品:
      df = pd.DataFrame({'mycol': list('axyebcolsdg')})
      elements = list('abcd')
          
      >>> idxall(df['mycol'], elements)
      10
      
      1. Henry 的样本 #1(混合顺序和重复):
      df = pd.DataFrame({'mycol': list('dxcabcodsdg')})
      elements = list('abcd')
      
      >>> idxall(df['mycol'], elements)
      5
      
      1. Henry 的样本 #2(未找到所有元素):
      df = pd.DataFrame({'mycol': list('dxcabcodsdg')})
      elements = list('abcz')
      
      >>> idxall(df['mycol'], elements)
      nan
      

      【讨论】:

      • 请注意,此 Categorical 方法仅在 elements 都是唯一的情况下才有效(尽管 OP 提供的示例确实如此)
      • @tdy。是的,但是有一个像 ['a', 'b', 'c', 'c', 'd'] 这样的列表很奇怪,不是吗?除非 OP 想要 2 个 'c' 实例但未指定。
      • 我不知道奇怪。它仅取决于用例。也许有人想确定在['e','e','e','u'] 用尽之前进入该列的深度。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2020-07-18
      • 2021-09-20
      • 2018-03-28
      • 2021-03-24
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多