python pandas-转换df以获取共享一个或多个属性的名称列表
从数据帧(df)开始,如下所示:python pandas-转换df以获取共享一个或多个属性的名称列表,python,python-3.x,pandas,Python,Python 3.x,Pandas,从数据帧(df)开始,如下所示: Name AttributeList A 1;2 B 2;3;1 C 4;7 D 8;7;3 我想为每一对可能的名称创建一个新的df,计算它们共享多少属性,并跳过它们不共享任何属性的情况。预期产出如下: Name1 Name2 NumberAttributesShared A B 2 B D 1 C D 1 配对不应该重复,所以如果我有一个B,那么我就不
Name AttributeList
A 1;2
B 2;3;1
C 4;7
D 8;7;3
我想为每一对可能的名称创建一个新的df,计算它们共享多少属性,并跳过它们不共享任何属性的情况。预期产出如下:
Name1 Name2 NumberAttributesShared
A B 2
B D 1
C D 1
配对不应该重复,所以如果我有一个B,那么我就不应该有B。
在本例中,A和C不共享任何属性,因此A和C对未列出。另一方面,对AB的值为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
。你明白了,可以尝试一些改进
level_0 level_1 NumberAttributesShared
0 A B 2
1 B D 1
2 C D 1
由于我们正在处理
str
和set
的循环和集合,因此您可能不想为此使用pandas
。使用纯python并在pd.DataFrame
中输入您的输出,最后假设您没有重复的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
。你明白了,可以尝试一些改进
level_0 level_1 NumberAttributesShared
0 A B 2
1 B D 1
2 C D 1
由于我们正在处理
str
和set
的循环和集合,因此您可能不想为此使用pandas
。在纯python中工作,并在pd.DataFrame中输入您的输出。最后从生成附加列开始,即属性列表的副本
,
但作为属性列表(而不是字符串或int):
然后,为了加快单个元素的读取速度,将Name
复制到索引:
df.set_index('Name', drop=False, inplace=True)
然后可以计算每个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])
最后,您可以生成结果:
result = pd.DataFrame(lst, columns=['Name1', 'Name2', 'NumberAttributesShared'])
当然,您应该从导入itertools开始
编辑
示例数据只包含带“;”分隔的字符串。
现在,正如您所指出的,属性列表可以包含一个单个数字,
我意识到单个字段的类型可以是string
或
int
要正确读取这两种情况下的属性,请在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
和H
都有一个公共属性(4)
这可能不是这些对象的最终结果,
因为它们的(单个)属性可以出现在其他属性的属性列表中
具有多个属性的对象
然后,您必须将上述“单件”与其他对象进行比较
使用多个属性,可能会添加一些常用属性
他们的帐户
剩下的部分是仅比较具有多个属性的对象,
正如我在开始时解释的,加入结果。所以至少
问题的规模将更小
自2019年2月9日起编辑
我的第一个解决方案完全基于熊猫,但结果证明它是可行的
比较慢
所以我想出了另一个更快的解决方案,基于Numpy和Pandas
这个想法是:
将Name列设置为df的索引:
将Attr列添加到df,其中包含数字列表(属性):
需要计算每个对象的属性向量
我们需要一个辅助功能:
def genAttrList(lst, len):
res = np.zeros(len, dtype='B')
for n in lst:
res[n] = 1
return res
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)
生成一个属性向量,属性向量的位置对应于
来自lst的属性(编号)。第二个参数(len)指定
此向量的长度-最大属性+1(不使用元素0)
注dtype='B'(无符号字节),基于以下假设:
属性的数目小于256。相比之下,它降低了内存需求
默认设置(在本例中)int类型
计算解决方案的函数是:
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'])
第一部分是计算attr数组(2-D)。每行代表数据
对于特定用户-编码为0和1序列的属性列表
然后按如下方式计算阵列计数:
- 按位_和是为两个attr实例计算的,带有额外的
轴在不同的地方。实际上,和是为每个
可能的一对对象
- 然后(根据上一步的结果)计算
count_非零,计算每个属性共有多少个属性
一对物体
此解决方案的威力来自:
- 事实上,按位_和是一个通用函数,它的运行速度非常快
比“普通”Python函数更快
- Numpy广播,这是一种非常有效的技术,几乎不需要花费太多时间
内存需求
- 使用添加的轴,允许对每对轴执行计算
列表中的对象
最后一步是计算(并返回)实际结果(DataFrame)。
值得注意的是:
- triu返回主数组中元素为零的计数数组
对角线及以下
- 非零返回上述数组中非零元素的索引
- 这样我们就可以从上三角得到非零元素的指数
计数(主对角线上方)。
这种限制
result = fun3(df)
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)
ordResult = ordered(result)
ordResult.equals(ordAnotherResult)