python pandas转换df以获取共享一个或多个属性的名称列表

2024-04-26 10:45:09 发布

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

从熊猫数据帧(df)开始,如下所示:

Name AttributeList
A      1;2
B      2;3;1
C      4;7
D      8;7;3

他们不想共享一对新属性的名字,我想跳过它们共享的属性。预期输出如下:

^{pr2}$

配对不应该重复,所以如果我有B,那么我就不应该有ba。 在本例中,未列出对A C,因为A和C不共享任何属性。另一方面,对A B具有值2,因为它们共享两个属性。在

有什么聪明有效的方法来达到这个目标?在


Tags: 数据方法name目标df属性名字ba
2条回答

假设没有重复的id,简单的解决方案如下

z = list(zip(names, map(set, atts.str.split(';').tolist())))

dict_ = dict()

for i in range(len(z)):
    for j in range(i+1, len(z)):
        inter = (z[i][1].intersection(z[j][1]))
        if inter:
            dict_[(z[i][0], z[j][0])] = len(z[i][1].intersection(z[j][1]))

pd.DataFrame(dict_, index=['NumberAttributesShared']).T.reset_index()

当然,在纯python中,不利用任何库作为itertools。你明白了,可以试着做些改进

^{pr2}$

因为我们使用的是strset的循环和集合,所以您可能不想为此使用pandas。在纯python中工作,最后在pd.DataFrame中输入输出

从生成一个附加列开始,它是AttributeList的副本, 但是作为一个属性列表(而不是字符串或int):

df['AttrList'] = df.AttributeList.astype(str).str.split(';')

然后,要加快单个元素的读取,请将Name复制到索引:

^{pr2}$

然后可以计算每个2元素的公共属性的数量 名称组合:

lst = []
for names in itertools.combinations(df1.Name, 2):
    n1, n2 = names
    s1 = set(df.at[n1, 'AttrList'])
    s2 = set(df.at[n2, 'AttrList'])
    cnt = len(s1.intersection(s2))
    if cnt > 0:
        lst.append([n1, n2, cnt])

最后,您可以生成结果:

^{4}$

当然,您应该从import itertools开始。在

编辑

您的示例数据只包含以“;”分隔的字符串。 现在,正如您所指出的,属性列表可以包含一个单个数字, 我意识到单个字段的类型可以是stringint。在

要正确读取这两种情况下的属性,请在df['AttrList'] = ... 说明将右侧改为:

df.AttributeList.astype(str).str.split(';')

(添加了.astype(str)以转换为正确的类型,我更改了这个细节 同上)。在

如何解决性能问题

提示(但不是完整的解决方案)如何加快计算速度。在

生成辅助表:

dfSgl = df[df.AttributeList.astype(str).str.isdigit()]

只包含具有单个属性的行。在

我的示例数据包含具有单个属性的四行:

['E', 2], ['F', 3], ['G', 4], ['H', 4]

所以在我的例子中,dfSgl包含:

     Name AttributeList
Name                   
E       E             2
F       F             3
G       G             4
H       H             4

然后执行:

dfSgl.groupby('AttributeList').filter(lambda x: len(x) > 1)

在这种情况下:

     Name AttributeList
Name                   
G       G             4
H       H             4

这意味着G和{}都有一个公共属性(4)。在

这可能不是这些物体的最终结果, 因为它们的(单个)属性可以出现在其他 具有多个属性的对象。在

然后你要把上面的“单子”和其他对象进行比较 对于多个属性,可以添加一些常用属性 对他们负责。在

剩下的部分是只比较具有多个属性的对象, 正如我在开头解释的那样,加入结果。所以至少 问题的规模会更小。在

编辑日期:2019年2月9日

我的第一个解决方案完全基于熊猫,但结果证明它是有效的 相对缓慢。在

所以我想出了另一个更快的解决方案,基于纽比和熊猫。在

其理念是:

  1. 名称列设置为df的索引:

    df.set_index('Name', inplace=True)
    
  2. Attr列添加到df,其中包含一个数字列表(属性):

    df['Attr'] = df.AttributeList.astype(str).str.split(';')\
        .map(lambda x: sorted(pd.to_numeric(x)))
    

它将需要计算每个对象的属性向量。在

  1. 我们需要一个辅助功能:

    def genAttrList(lst, len):
        res = np.zeros(len, dtype='B')
        for n in lst:
            res[n] = 1
        return res
    

生成一个属性向量,其位置对应于 属性(数字)来自lst。第二个参数(len)指定 此向量的长度-最大属性+1(不使用元素0)。在

注意dtype='B'(无符号字节),基于假设 属性数小于256。相比之下,它降低了内存需求 默认值(在本例中)int类型。在

  1. 计算解的函数为:

    def fun3(df):
        ind = df.index
        attrLen = df.Attr.map(lambda x: x[-1]).max() + 1
        attr = np.array(df.Attr.transform(lambda x:
            genAttrList(x, attrLen)).tolist())
        counts = np.count_nonzero(np.bitwise_and(
            attr[np.newaxis, :], attr[:, np.newaxis]), axis=(2))
        return pd.DataFrame(data=[ (ind[x[0]], ind[x[1]], counts[x])
            for x in zip(*np.nonzero(np.triu(counts, 1)))],
            columns=['Name1', 'Name2', 'NumberAttributesShared'])
    

第一部分是计算二维数组。每行代表数据 对于特定用户-一个由0和1组成的序列编码的属性列表。在

然后计数数组计算如下:

  • 对于两个attr的实例,使用附加的 轴心在不同的地方。实际上,是针对 可能的一对对象。在
  • 然后(根据上一步的结果)计算 count_non zero,计算每个属性共有多少个属性 一对物体。在

此解决方案的强大功能源于:

  • 按位_和是一个通用函数,它跑得很多 比“普通”Python函数更快。在
  • Numpy广播,这是一种非常有效的技术 内存要求。在
  • 使用附加轴,允许对每对轴进行计算 列表中的对象。在

最后一步是计算(并返回)实际结果(DataFrame)。 值得注意的是:

  • triu返回counts数组,其中main元素为零 对角线和下方。在
  • nonzero返回上述数组中非零元素的索引。在
  • 这样我们就可以从上三角得到非零元素的指数 of计数(主对角线上方)。 此限制允许避免与“交换”对象标签重复。在
  • zip创建一个返回行/列对的迭代器。在
  • ind[x[0]ind[x[1]将数组坐标转换为对象名。在
  • counts[x]从counts读取相应的元素(数字 共同属性)。在

所以唯一剩下的就是打电话:

result = fun3(df)

我比较了执行时间和我的初始解决方案(使用%timeit), 在500个对象的样本数据中,从50个列表中最多有5个属性, 结果发现,这个解决方案速度快了10倍。在

最初我建议对 一个单独的属性,然后每个单独的属性都有多个属性,最后 每个对象都有多个属性(functionfun2not 包括在这里)。 但事实证明,这种解决方案的工作时间是前者的2.5倍 所以我撤销了这个命题。在

如果要将此结果与执行的计算结果进行比较 对于另一种方法,请使用以下“排序函数”:

def ordered(df):
    res = df.copy()
    res[['Name1', 'Name2']] = np.sort(res[['Name1', 'Name2']].values, axis=1)
    return res.sort_values(['NumberAttributesShared', 'Name1', 'Name2'],
        ascending=[False, True, True]).reset_index(drop=True)

它将Name1Name2交换,并对行进行排序 NumberAttributesShared(降序),然后在两个名称上。 最后一步是重置索引。在

因此,您应该(针对每个结果)致电:

ordResult = ordered(result)

从各种方法得到的有序结果(或result或其他结果),您可以检查它们是否相同:

ordResult.equals(ordAnotherResult)

最后一点:我完全知道这个编辑包含了很多新内容 信息,尤其是来自Numpy区域的信息。 但不要因为新概念的“雪崩”而感到害怕。 把每一个新的或鲜为人知的问题,一个接一个, 并在网上搜索进一步的解释。在

相关问题 更多 >