获取行之间的 jaccard 距离,而无需在 python 中的列上预先进行一种热编码

Get jaccard distance between rows without preformong one hot encoding on the column in python

我有一个 table 看起来像这样:

id   feature_1  feature_2  feature_3
1       A          x          r
23      A          v          r
56      B          z          r

我想以一种有效的方式在行 (id) 之间创建某种 Jaccard 距离,而不是将特征转换为一个热编码列(很多可能性)。

如何得到行间相似度总和的结果?类似的东西:

id_1  id_2  similarity  num_of_features  distance
 1     23       2              3            2/3
 1     56       1              3            1/3
 23    56       1              3            1/3

我的代码:

def create_pairs(ids_list):
  pairs_list = []
  for (p1,p2) in itertools.combinations(ids_list,2):
      pair = [p1,p2]
      pairs_list.append(pair)

  return pairs_list

def get_distance(id_1, id_2, df):

    ????

    return distance


ids_list= list(df['id'].unique())
pairs_list = create_pairs(ids_list=ids_list)
pairs_df = pd.DataFrame(pairs_list,columns=['id_1','id_2'])
distance_list = []
for [id_1, id_2] in pairs_list:
  distance = get_distance(id_1=id_1, id_2=id_2,df=df)

pairs_df['distance'] = distance_list

我相信 scikit 中没有 row-wise 内置的 jaccard 距离功能,但您可以尝试以下操作:

from sklearn.metrics import jaccard_score
import pandas as pd 

data = [['a', 'x', 'r'], ['a', 'v', 'r'], ['b', 'z', 'r']] 
  
# Create the pandas DataFrame 
df = pd.DataFrame(data, columns = ['feature_1',  'feature_2',  'feature_3'])

print(df)
df_cmp = df.copy()
for index, row in df.iterrows():
  for index_, row_ in df_cmp.iterrows():
    if index == index_:
      continue
    print(index, index_, jaccard_score(row,row_, average='weighted'))
  df_cmp = df_cmp.drop(index) #remove after as done already
 

创建一个您将使用的副本 - 这样您就可以删除已经传递的行。然后您可以计算行的 jaccard 距离。输出:

  feature_1 feature_2 feature_3
0         a         x         r
1         a         v         r
2         b         z         r

行分数:

0 1 0.6666666666666666
0 2 0.3333333333333333
1 2 0.3333333333333333

您可以不用打印,而是将其写入数据框,稍后再使用。然而这种方法有复杂性O(n*log(n)),也许会有更快的方法。

编辑:

不知何故我没有注意到你已经创建了对,因此只需将你的问号替换为:

distance = jaccard_score(df.iloc[id_1],df.iloc[id_2], average='weighted')

试试这个,应该会更快:

def get_distance(id_1, id_2, df):

    similarity_count = df.loc[id_1]==df.loc[id_2]
    similarity_count = similarity_count.sum(0)
    distance = 1-(similarity_count /len(list(df.columns)))

    return distance