将列表与DataFrame中的每条记录进行比较

2024-05-15 13:04:04 发布

您现在位置:Python中文网/ 问答频道 /正文

我有一个用例,将同一列中的列表与其自身进行比较,代码如下:

for i in range(0,len(counts95)):
    for j in range(i+1,len(counts95)):
        for x in counts95['links'][i]:
            for y in counts95['links'][j]:
                if x == y and counts95['linkoflinks'][j] is None:
                    counts95['linkoflinks'][j] = counts95['index'][i]

代码可以工作,但对python不友好(使用4表示循环),并且需要花费大量的时间来执行操作。 它背后的主要思想是将列表中95['links']列中的元素位于任何一行的记录链接起来,如果是,则仅当linksoflinks列为None(无覆盖)时,才使用第一列的索引更新列linksoflinks

请参阅下面的参考表:

counts95 = pd.DataFrame({'index': [616351, 616352, 616353,6457754], 
                   'level0': [25,30,35,100],
                   'links' : [[1,2,3,4,5],[23,45,2],[1,19,67],[14,15,16]],
                   'linksoflinks' : [None,None,None,None]})

编辑: 新数据帧

counts95 = pd.DataFrame({'index': [616351, 616352, 616353,6457754,6566666,464664683], 
                   'level0': [25,30,35,100,200,556],
                   'links' : [[1,2,3,4,5],[23,45,2],[1,19,67],[14,15,16],[1,14],[14,1]],
                   'linksoflinks' : [None,None,None,None,None,None]})

期望输出:

     index  level0            links  linksoflinks
0   616351      25  [1, 2, 3, 4, 5]         NaN
1   616352      30      [23, 45, 2]    616351.0
2   616353      35      [1, 19, 67]    616351.0
3  6457754     100     [14, 15, 16]         NaN
4  6566666     200           [1,14]    616351.0
5  6457754     556           [14,1]    616351.0

Tags: 代码innonedataframe列表forindexlen
3条回答

使用explodeduplicated.map分配给重复的链接值,但只分配后者

df = counts95.explode('links')


m = df[df.duplicated(subset=['links'],keep=False)].groupby('links')['index'].first()


df['link_above'] = df['links'].loc[df.duplicated(subset='links',keep='first')].map(m)



re_made_df = df.groupby(["index", "level0"]).agg(
    links=("links", list), linkoflist=("link_above", "first")).reset_index()


print(re_made_df)


     index  level0            links  linkoflist
0   616351      25  [1, 2, 3, 4, 5]         NaN
1   616352      30      [23, 45, 2]    616351.0
2   616353      35      [1, 19, 67]    616351.0
3  6457754     100     [14, 15, 16]         NaN

好的模式是为您的任务使用适当的数据结构。回答问题«Y序列中是否存在元素X»的最佳选择是内置的set。当您的集合是不可变的时,考虑使用^ {CD2}}。

解决方案

以下是我将如何以pythonic的方式解决这个问题:

# necessary imports
from collections import defaultdict
from typing import Tuple, FrozenSet, DefaultDict

# initialise the links "mapping": for every index save frozenset of its links
links: Tuple[Tuple[int, FrozenSet[int]]] = (
    # tuple of tuples is like a dict but will let you iterate by index
    (616351, frozenset((1, 2, 3, 4, 5))),
    (616352, frozenset((23, 45, 2))),
    (616353, frozenset((1, 19, 67))),
    (6457754, frozenset((14, 15, 16))),
)

# defaultdict automatically creates new lists
#   as you access its keys which are not yet present
links_of_links: DefaultDict[int, List[int]] = defaultdict(list)

for i, item in enumerate(links):
    key, values = item  # split tuple into individual elements
    next_rows = links[i+1:]  # we will iterate over succeeding rows
    for next_key, next_values in next_rows:
        # here we check sets intersection:
        #   it is non-empty if any common elements are present
        if values & next_values:
            # though key might not be present in links_of_links,
            #   defaultdict will autocreate a new empty list
            links_of_links[key].append(next_key)

链接的内容链接的内容:defaultdict(<class 'list'>, {616351: [616352, 616353]})

复杂性

现在让我们比较一下您的解决方案和我的解决方案的复杂性,以证明后者更有效。让我们假设N是行数L是链接列表的某种长度(平均值或最大值,这并不重要)。您的解决方案大致比较了所有行对,这给了我们O(N * N)。然后乘以两个列表的简单比较的复杂性-O(L * L)。它总共给了我们{}

建议的解决方案仍然交叉连接所有行,因此N * N与我们同在。但是现在我们用一种更有效的方式来比较集合本身:O(min(L, L)) === O(L),正如Python Time Complexity所说。因此,总体复杂性除以单个L,将O(N² * L)作为总数

与示例dataframe构造函数相比,所需的输出使用不同的值和列名。我使用您想要的输出数据帧进行测试

逻辑:
对于links的每个子列表,我们需要找到第一个重叠子列表的行索引(我指的是数据帧的索引,而不是列index)。我们将使用这些行索引在counts95上按.loc切片,以获得列index的相应值。为了实现这一目标,我们需要采取以下几个步骤:

  • 将每个子列表与link中的所有子列表进行比较。列表理解是 快速有效地完成这项任务。我们需要编写一份清单 理解如何创建布尔二维掩码数组,其中每个子数组 包含重叠行的True值和非重叠行的False值(请参阅本指南的分步说明) 2D遮罩并检查列links您将看到更清晰的内容)
  • 我们希望从顶部到当前子列表进行比较。即站立 从当前行开始,我们只想向后比较顶部。 因此,我们需要设置与False相比的任何前向。这是 {}的功能
  • 在这个2D掩模的每个子阵列中,True的位置/索引是 当前子列表重叠的行的行索引。我们需要找到 这些位置是True。它是np.argmax的功能。 np.argmax返回数组的第一个max元素的位置/索引True被认为是{},而{}被认为是{}。因此 在具有True的任何子数组上,它正确返回第一个重叠行索引。但是,在所有False子数组上,它返回0。稍后我们将使用where处理所有False子阵列
  • np.argmax之后,2D掩码被缩减为1D掩码。每个元素 此1D掩码是重叠子列表的行索引数。 将其传递给.loc以获取列index的相应值。 然而,结果也错误地包括了 二维掩码包含所有False。我们希望这些行变成NaN。它是 {}的功能

方法1
使用列表理解在links的每个列表和links中的所有列表之间构建布尔二维掩码m。我们只需要向后比较,所以使用np.tril将掩码的右上角三角形压碎为表示向前比较的所有False。最后,调用np.argmax以获取m的每一行中第一个True的位置,并链接where以将m的所有False行转换为NaN

c95_list = counts95.links.tolist()
m = np.tril([[any(x in l2 for x in l1) for l2 in c95_list] for l1 in c95_list],-1)
counts95['linkoflist'] = (counts95.loc[np.argmax(m, axis=1), 'index']
                                  .where(m.any(1)).to_numpy())

 Out[351]:
     index  level0            links  linkoflist
0   616351      25  [1, 2, 3, 4, 5]         NaN
1   616352      30      [23, 45, 2]    616351.0
2   616353      35      [1, 19, 67]    616351.0
3  6457754     100     [14, 15, 16]         NaN
4  6566666     200          [1, 14]    616351.0
5  6457754     556          [14, 1]    616351.0

方法2
如果dataframe很大,则将每个子列表仅与links的顶部进行比较会更快。在大数据帧上,它可能比方法1快2倍

c95_list = counts95.links.tolist()
m = [[any(x in l2 for x in l1) for l2 in c95_list[:i]] for i,l1 in enumerate(c95_list)]
counts95['linkoflist'] = counts95.reindex([np.argmax(y) if any(y) else np.nan 
                                                   for y in m])['index'].to_numpy()

逐步(方法1)

m = np.tril([[any(x in l2 for x in l1) for l2 in c95_list] for l1 in c95_list],-1)

Out[353]:
array([[False, False, False, False, False, False],
       [ True, False, False, False, False, False],
       [ True, False, False, False, False, False],
       [False, False, False, False, False, False],
       [ True, False,  True,  True, False, False],
       [ True, False,  True,  True,  True, False]])

argmax返回所有False行的第一个True和第一个False位置

In [354]: np.argmax(m, axis=1)
Out[354]: array([0, 0, 0, 0, 0, 0], dtype=int64)

使用argmax的结果进行切片

counts95.loc[np.argmax(m, axis=1), 'index']

Out[355]:
0    616351
0    616351
0    616351
0    616351
0    616351
0    616351
Name: index, dtype: int64

where以将所有False对应的行从m转到NaN

counts95.loc[np.argmax(m, axis=1), 'index'].where(m.any(1))

Out[356]:
0         NaN
0    616351.0
0    616351.0
0         NaN
0    616351.0
0    616351.0
Name: index, dtype: float64

最后,输出的索引不同于counts95的索引,因此只需调用to_numpy即可获得要分配给counts95linkoflist列的数据数组

相关问题 更多 >