Сходство косинусов для двух датапарков pyspark

1

У меня есть PySpark DataFrame, df1, который выглядит так:

CustomerID  CustomerValue CustomerValue2 
12          .17           .08

У меня есть второй PySpark DataFrame, df2

 CustomerID  CustomerValue CustomerValue
 15          .17           .14
 16          .40           .43
 18          .86           .09

Я хочу взять сходство с косинусом двух данных. И что-то вроде этого

 CustomerID  CustomerID   CosineCustVal CosineCustVal
 15          12           1            .90
 16          12           .45          .67
 18          12           .8           .04
  • 0
    Вы пробовали что-нибудь? datascience.stackexchange.com/questions/13347/...
  • 0
    Да, к сожалению, но это не могло работать. Другое решение, которое я подумал, состояло в том, чтобы преобразовать первый df в списке и взять косинусное сходство, но я бы хотел избежать этого
Показать ещё 3 комментария
Теги:
apache-spark
apache-spark-sql
pyspark

1 ответ

0

Вы можете вычислить подобие косинуса только для двух векторов, а не для двух чисел. Тем не менее, если столбцы CustomerValue представляют собой различные компоненты вектора, который представляет функцию, которую вы хотите получить для двух клиентов, вы можете сделать это, переставив кадр данных, а затем выполните объединение на CuatomerValues.

Транспонирование может быть выполнено с помощью взрыва (более подробная информация о переносе кадра данных здесь):

from pyspark.sql import functions as F

kvs = F.explode(F.array([
        F.struct(F.lit(c).alias('key'), F.columm(c).alias('value')) for c in ['CustomerValue1', 'CustomerValue2']
      ])).alias('kvs')

dft1 = (df1.select(['CustomerID', kvs])
        .select('CustomerID', F.column('kvs.name').alias('column_name'), F.column('kvs.value').alias('column_value'))
        )
dft2 = (df2.select(['CustomerID', kvs])
        .select('CustomerID', F.column('kvs.name').alias('column_name'), F.column('kvs.value').alias('column_value'))
        )

где dft1 и dft2 обозначают транспонированные кадры данных. После их переноса вы можете присоединиться к ним по именам столбцов:

dft2 = (dft2.withColumnRenamed('CustomerID', 'CustomerID2')
        .withColumnRenamed('column_value', 'column_value2')
       )
cosine = (dft1.join(dft2, dft1.column_name = dft2.column_name)
          .groupBy('CustomerID' , 'CustomerID2')
          .agg(F.sum(F.column('column_value')*F.column('column_value2')).alias('cosine_similarity'))
         )

Теперь в cosine вас есть три столбца: CustomerID из первого и второго кадров данных и сходство косинусов (при условии, что значения сначала были нормализованы). Это приводит к тому, что у вас есть только строки для пар CustomerID, которые имеют ненулевое сходство (в случае нулевых значений для некоторых идентификаторов клиентов). Для вашего примера:

df1:

CustomerID CustomerValue CustomerValue2
12         .17           .08

df2:

CustomerID CustomerValue CustomerValue
15         .17           .14
16         .40           .43
18         .86           .09

косинус:

CustomID CustomID2 cosine_similarity
12       15        .0401
12       16        .1024
12       18        .1534

Конечно, это еще не настоящее сходство в косинусах, вам нужно сначала нормализовать значения. Вы можете сделать это с помощью группы:

(df.groupBy('CustomerID')
 .agg(F.sqrt(F.sum(F.column('column_value')*F.column('column_value'))).alias('norm'))
 .select('CustomerID', F.column('column_name'), (F.column('column_value')/F.column('norm')).alias('column_value_norm'))
)

После нормализации столбцов ваши сходства с косинусом становятся следующими:

CustomID CustomID2 cosine_similarity
12       15        .970
12       16        .928
12       18        .945

Большие значения сходства обусловлены низкой размерностью (только для двух компонентов).

Ещё вопросы

Сообщество Overcoder
Наверх
Меню