У меня есть 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
Вы можете вычислить подобие косинуса только для двух векторов, а не для двух чисел. Тем не менее, если столбцы 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
Большие значения сходства обусловлены низкой размерностью (только для двух компонентов).