将数据帧行转换为Python集
我有以下数据集:将数据帧行转换为Python集,python,pandas,dataframe,set,similarity,Python,Pandas,Dataframe,Set,Similarity,我有以下数据集: import pandas as pd import itertools A = ['A','B','C'] M = ['1','2','3'] F = ['plus','minus','square'] df = pd.DataFrame(list(itertools.product(A,M,F)), columns=['A','M','F']) print(df) 示例输出如下所示: A M F 0 A 1 plus 1 A 1
import pandas as pd
import itertools
A = ['A','B','C']
M = ['1','2','3']
F = ['plus','minus','square']
df = pd.DataFrame(list(itertools.product(A,M,F)), columns=['A','M','F'])
print(df)
示例输出如下所示:
A M F
0 A 1 plus
1 A 1 minus
2 A 1 square
3 A 2 plus
4 A 2 minus
5 A 2 square
我想对这个数据帧中的每一行进行两两比较(jaccard相似性),例如比较
a1加
和a2平方
并获得两者之间的相似值
我编写了一个jaccard函数:
def jaccard(a, b):
c = a.intersection(b)
return float(len(c)) / (len(a) + len(b) - len(c))
因为我使用了intersection
我希望输出如下(此预期结果值只是随机数):
获得成对度量结果的最佳方法是什么
谢谢,可以在此处找到所需内容的完整实现:
series_set = df.apply(frozenset, axis=1)
new_df = series_set.apply(lambda a: series_set.apply(lambda b: jaccard(a,b)))
通过对函数进行矢量化,可以摆脱嵌套的apply。首先,获取所有成对组合并将其传递给函数的向量化版本-
def jaccard_similarity_score(a, b):
c = a.intersection(b)
return float(len(c)) / (len(a) + len(b) - len(c))
i = df.apply(frozenset, 1).to_frame()
j = i.assign(foo=1)
k = j.merge(j, on='foo').drop('foo', 1)
k.columns = ['A', 'B']
fnc = np.vectorize(jaccard_similarity_score)
y = fnc(k['A'], k['B']).reshape(len(df), -1)
这已经快了,但是让我们看看我们是否能更快
使用senderle的fast-
你说的“它不起作用”是什么意思?对于给定的
df
,set(df.loc[0])
的计算结果与预期一致,为{'1','A','plus'}
。在我的笔记本中,结果是{'A','M','F'}
,这是因为您有[[0]
而不是[0]
,它将返回一个数据帧对象,而不是一个序列对象。由于它是一个数据帧,set
将返回列值。好的,谢谢@Sebastian,我刚刚意识到我使用了双括号。如何计算第0行和第1行的0.43?三个项目中的两个相交,那么它不应该是2/(3+3-2)=0.5吗?谢谢你,塞巴斯蒂安,我已经尝试了一个多小时的结果,但我并不认为这会这么容易,我刚刚开始使用apply
来获得“足够好”的解决方案。嗨@Sebastian,有可能对角删除一半结果吗?因为它是重复的,对吗?嵌套应用?哇,这在大的输入上会受到影响。当然,但是有没有更简单的方法可以将两个系列的笛卡尔乘积转换成具有自定义函数的数据帧?还是有更好的方法来解决这个问题?正如我所说,这是我“足够好”的方法,但我希望看到一个更精确的答案。肯定是一个更好的解决方案,+1。不过,您不能否认嵌套的apply
s的简单性,它应该只是一个常数。另外,我编辑了我的答案以使用frozenset
,完全忘记了这一点。@Sebastian承认是的,但我打赌常数因子相当大,你应该看到中等大小输入的差异(是的,对于大输入,由于问题的组合性质,这最终变得很慢)。此外,我不确定这其中计算成本高的部分是什么,但假设它应用了fnc
,只将其应用于上三角,就可以将时间减少大约两倍。@Sebastian,而且函数本身速度很慢。此外,使用冻结集的列在性能方面没有任何好处,因为它们是对象。这就是OP输入的性质。是的,只计算上三角应该可以提供更多的速度增益。
def jaccard_similarity_score(a, b):
c = a.intersection(b)
return float(len(c)) / (len(a) + len(b) - len(c))
i = df.apply(frozenset, 1).to_frame()
j = i.assign(foo=1)
k = j.merge(j, on='foo').drop('foo', 1)
k.columns = ['A', 'B']
fnc = np.vectorize(jaccard_similarity_score)
y = fnc(k['A'], k['B']).reshape(len(df), -1)
y
array([[ 1. , 0.5, 0.5, 0.5, 0.2, 0.2],
[ 0.5, 1. , 0.5, 0.2, 0.5, 0.2],
[ 0.5, 0.5, 1. , 0.2, 0.2, 0.5],
[ 0.5, 0.2, 0.2, 1. , 0.5, 0.5],
[ 0.2, 0.5, 0.2, 0.5, 1. , 0.5],
[ 0.2, 0.2, 0.5, 0.5, 0.5, 1. ]])
def cartesian_product(*arrays):
la = len(arrays)
dtype = numpy.result_type(*arrays)
arr = numpy.empty([len(a) for a in arrays] + [la], dtype=dtype)
for i, a in enumerate(numpy.ix_(*arrays)):
arr[...,i] = a
return arr.reshape(-1, la)
i = df.apply(frozenset, 1).values
j = cartesian_product(i, i)
y = fnc(j[:, 0], j[:, 1]).reshape(-1, len(df))
y
array([[ 1. , 0.5, 0.5, 0.5, 0.2, 0.2],
[ 0.5, 1. , 0.5, 0.2, 0.5, 0.2],
[ 0.5, 0.5, 1. , 0.2, 0.2, 0.5],
[ 0.5, 0.2, 0.2, 1. , 0.5, 0.5],
[ 0.2, 0.5, 0.2, 0.5, 1. , 0.5],
[ 0.2, 0.2, 0.5, 0.5, 0.5, 1. ]])