У меня есть фрейм данных pandas (df), который имеет три столбца (пользователь, значения и имя группы), столбец значений с несколькими значениями, разделенными запятыми, в каждой строке.
df = pd.DataFrame({'user': ['user_1', 'user_2', 'user_3', 'user_4', 'user_5', 'user_6'],
'values': [[1, 0, 2, 0], [1, 8, 0, 2],[6, 2, 0, 0], [5, 0, 2, 2], [3, 8, 0, 0],[6, 0, 0, 2]],
'group': ['B', 'A', 'C', 'A', 'B', 'B']})
df
вывод:
user values group
0 user_1 [1, 0, 2, 0] B
1 user_2 [1, 8, 0, 2] A
2 user_3 [6, 2, 0, 0] C
3 user_4 [5, 0, 2, 2] A
4 user_5 [3, 8, 0, 0] B
5 user_6 [6, 0, 0, 2] B
Затем я вычисляю среднее значение каждого кластера, который называется центроидом в фрейме данных (df1).
df1 = (df.groupby('group', as_index=False)['values']
.agg(lambda x: np.vstack(x).mean(0).round(2))
)
df1
Вывод:
group values
0 A [3.0, 4.0, 1.0, 2.0]
1 B [3.33, 2.67, 0.67, 0.67]
2 C [6.0, 2.0, 0.0, 0.0]
Наконец, я вычисляю среднее расстояние от каждого пользователя до всех кластеров в следующем коде, используя евклидово расстояние.
for value in df['values']:
distance_values = []
for centroid in df1['values']:
distance_values.append(distance.euclidean(value, centroid))
print(distance_values)
Вывод:
[5.0, 3.8439042651970405, 5.744562646538029]
[4.58257569495584, 6.004631545732011, 8.06225774829855]
[4.242640687119285, 2.9112883745860696, 0.0]
[4.58257569495584, 3.668187563361503, 3.605551275463989]
[4.58257569495584, 5.4236150305861495, 6.708203932499369]
[5.0990195135927845, 4.059014658756482, 2.8284271247461903]
Итак, для каждого пользователя я рассчитываю среднее расстояние до центра тяжести каждого кластера.
Например:
Для user_1 среднее расстояние до кластеров A=5,0, B=3,8439042651970405 и C=5,744562646538029.
Как вернуть максимальное значение каждой строки в значениях расстояния с именем его кластера в фрейме данных?
Например, ожидаемый результат:
user max_value group
0 user_1 5.744562646538029 C
1 user_2 8.06225774829855 C
2 user_3 4.242640687119285 A
3 user_4 4.58257569495584 A
4 user_5 6.708203932499369 C
5 user_6 5.0990195135927845 A
Вы можете использовать apply
для извлечения максимальных значений с их индексами.
а затем используйте основные манипуляции со строками:
df['distance_values'] = [[5.0, 3.8439042651970405, 5.744562646538029],
[4.58257569495584, 6.004631545732011, 8.06225774829855],
[4.242640687119285, 2.9112883745860696, 0.0],
[4.58257569495584, 3.668187563361503, 3.605551275463989],
[4.58257569495584, 5.4236150305861495, 6.708203932499369],
[5.0990195135927845, 4.059014658756482, 2.8284271247461903]]
max_df = df['distance_values'].apply(lambda x: [max(x), x.index(max(x))])
df['max_value'] = max_df.str[0]
df['group'] = max_df.str[1].map(dict(zip(range(4), 'ABC')))
max_dist_idx = []
distant_cluster = []
for value in df['values']:
distance_values = []
for centroid in df1['values']:
distance_values.append(distance.euclidean(value, centroid))
max_dist_idx.append(max(distance_values))
distant_cluster.append(distance_values.index(max(distance_values)))
cluster_map = {0: 'A', 1: 'B', 2: 'C'}
max_group = [cluster_map[i] for i in distant_cluster]
то вы можете просто смонтировать свой фрейм данных:
pd.DataFrame(data = {'user': df.user,
'max_value': max_dist_idx,
'group': max_group})
user max_value group
0 user_1 5.744563 C
1 user_2 8.062258 C
2 user_3 4.242641 A
3 user_4 4.582576 A
4 user_5 6.708204 C
5 user_6 5.099020 A
Вы также можете включить вычисление евклидова расстояния в функцию, которую вы будете применять для большей эффективности:
def calc_max_dist(value):
dist_series = df1['values'].apply(lambda x: distance.euclidean(value, x))
return dist_series.max(), df1[dist_series == dist_series.max()]['group'].values
df[['max_value', 'closest_group(s)']] = pd.DataFrame(df['values'].apply(calc_max_dist).tolist())
Вывод:
user values group max_value closest_group(s)
0 user_1 [1, 0, 2, 0] B 5.744563 [C]
1 user_2 [1, 8, 0, 2] A 8.062258 [C]
2 user_3 [6, 2, 0, 0] C 4.242641 [A]
3 user_4 [5, 0, 2, 2] A 4.582576 [A]
4 user_5 [3, 8, 0, 0] B 6.708204 [C]
5 user_6 [6, 0, 0, 2] B 5.099020 [A]
@ Нури Таш, могу ли я хранить и сохранять (distance_values) и (max_df) в отдельных фреймах данных? для простоты использования и интерпретации?