Розробка сценарію для порівняння смаків людей

Вітаю, %username%. Сьогодні розробимо скрипт складання рейтингу схожості інтересів між людьми.

Зацікавилися? Прошу під кат



Замість вступу



Хотілося б зауважити, що цей спосіб працює ефективно тільки для найбільшого відносини між кількістю характеристик набору і числом наборів. Інакше рекомендую використовувати даний спосіб

Теорія



Отже, почнемо здалеку. Уявімо два деяких набору даних. Назвемо їх p та q. Нехай кожен з цих наборів характеризують два числа. Тоді представимо ці набори, як точки в просторі L з розмірністю dimL = n, де n — кількість характеристик. В даному випадку 2

p(p1; p2) та q(q1; q2)

Визначимо деяку метрику d(p, q) = k, де k — коеф. відмінності двох наборів. Визначимо метрику як евклідова відстань між цими двома точками, тобто, з курсу ангема ми знаємо:



На нашу визначення випливає, що різниця між двома наборами є відстань між точками, яким ми звіряємо наші набори. Тоді різниця між двома наборами даних знаходиться за теоремою Піфагора, про як!

Тоді для двох ідентичних наборів відстань дорівнюватиме нулю.

І що це все означає?


Розглянемо на прикладі. Візьмемо двох випробовуваних, назвемо їх Вася (В) і Коля (До). Задамо їм питання:

1) — Оцініть за 10 бальною шкалою наскільки вам подобаються персики
2) Оцініть за 10 бальною шкалою наскільки вам подобається полуниця

Припустимо, що Вася і Коля відповіли однаково. Тоді, очевидно, відстань між точками буде дорівнює нулю, тобто в даних наборах їх інтересів/смаків вони ідентичні. Розглянемо тепер випадок різних відповідей.

В: (1) дав 5, (2) дав 8
До: (3) дав 10, на (2) дав 0

Тоді можемо представити у двовимірному просторі точки для Колі і Васі:

В(5; 8) і(10; 0), відстань між ними, як нескладно порахувати 9.4. Це і є коеф. відмінності. Але… стривайте, як же його інтерпретувати?

Давайте подивимося. Мінімальна різниця дорівнює нулю при повному збігу наборів, це зрозуміло. А як же бути з максимальним? Розглянемо на нашій площині деяку дельта-околиця. так як максимальна кількість балів 10, то дельта буде дорівнює 10, тобто за теоремою Піфагора sqrt(100 + 100) = 14.14 — це і є максимальна різниця, за яким набори даних можна вважати протилежними. Таким чином, у Колі і Васі в даному випадку більше відмінностей, ніж подібності.

І навіщо це все?



Застосування можна знайти де завгодно. Сайти знайомств, сайти по фрілансу, сайти вакансій і т. д. Створюючи опитувальники можна створити деяку мапу інтересів і смаків з якої можна знаходити пари для відносин. Любовних, дружніх, трудових, будь-яких.

Реалізуємо на прикладі картографування інтересів людей. І відразу протестуємо, на прикладі моїх друзів.

Використовувати будемо python, так як даний ЯП найбільш підходить для реалізації подібних алгоритмів. В першу чергу із-за зручності роботи зі словниками ( хеші/асоціативні масиви ), а також завдяки шикарному вбудованому модулю росол, який дозволить нам зберігати словники з питаннями-відповідями прямо на диск і потім використовувати. За традицією, весь код можна буде подивитися в кінці статті

Для розрахунку метрики будемо використовувати наступний код:

Розрахунок метрики
def calc(nPoint):
result = 0.0

print("sqrt(", end="")
for key in Dictionary:
print("(", Points[nPoint[0]][key], " - ", Points[nPoint[1]][key], ")^2 + ", sep="", end="")
result = result + math.pow((Points[nPoint[0]][key] - Points[nPoint[1]][key]),2)
print(")")
result = math.sqrt(result)
return result



Функція приймає кортеж з двох чисел, які говорять які набори даних аналізувати ( набори даних зберігаються в словнику словника, де ключі — номер набору ), які знаходяться в словнику Points.

Функція повертає коеф. відмінності двох наборів.

Для того, щоб «картографировать» інтереси нам треба проаналізувати кожні набори, а не тільки два. Для цього є функція:

Генерація коеф. для кожного набору
def GenerateMap():
print("~~~~")
for i in range(1, PSize):
for j in range(i + 1, PSize + 1):
print(i, " and ", j, " = ", calc( (i, j) ), sep="")



Функція генерує карту відносин наборів і виводить в stdout.

Тестування скрипта на людях

Охохо, як звучить. Тепер пограємося. Складемо список питань:

Список
1) Наскільки вам цікаві політичні новини в світі? (0-не цікаві, 10 - цікаві) 
 
2) Як сильно ви цікавитеся архітектурою(0-не цікавлюся, 10 сильно цікавлюся) 
 
3) Наскільки сильно вам подобаються фільми жахів?(0-не подобаються , 10-дуже подобаються) 
 
4) Наскільки сильно вам подобаються наукові статті?(0-не подобаються , 10-дуже подобаються) 
 
5) Ваші інтереси до біологічних наук? (0-не цікавлюся, 10 сильно цікавлюся) 
 
6) Ваші інтереси до історії? (0-не цікавлюся, 10 сильно цікавлюся) 
 
7) Ваші інтереси до протилежної статі? (0-не цікавлюся, 10 сильно цікавлюся) 
 
8) Ваші інтереси до читання? (0-не цікавлюся, 10 сильно цікавлюся) 
 
9) Ваші інтереси до хімії? (0, 10) 
 
10) Ваші інтереси до психології? ( 0, 10)
 
11) До програмування (0, 10)
 
12) До фізики (0, 10)
 
13) чи Любите ви самоосвіта(0, 10)
 



І дамо на них відповісти кожному користувачеві, створивши набір даних. Відразу скажу, в програмі набір даних номеруется по мірі їх завантаження через pickle. Тому і виводиться, відповідно номери у форматі (номер = коеф. відмінності ).

Для зручності читання я їх вручну передрукував у прізвища, замінивши для статті рандомные ( згода на обр. даних було отримано не від усіх ).

Запустивши картографування отримуємо наступне:

Вихлоп
Іванов - Семенщенко= 11.83
 
Іванов - Кирилов= 12.72
 
Іванов - Козлов = 12.92
 
Іванов - Азарова = 12.88
 
Іванов - Петрова = 16.49
 

 
Семенщенко - Кирилов= 9.59 
 
Семенщенко - Козлов = 8.77
 
Семенщенко - Азарова = 10
 
Семенщенко - Петрова = 14.28
 

 
Кирилов - Козлов = 10.34
 
Кирилов - Азарова = 13.85
 
Кирилов - Петрова = 12.4
 

 
Козлов - Азарова = 12.68
 
Козлов - Петрова = 14.93
 

 
Азарова - Петрова = 17.66
 



Як це інтерпретувати? Давайте подивимося, найбільше запитань було у нас 13. Максимальна кількість балів — 10

Тоді по теоремі Піфагора знайдемо найбільшу можливу відстань в окретности 13-вимірного простору:

sqrt(100 * 13) = 36,056
Середнє значення = максимальна / 2 = 16.03

Таким чином, ми бачимо, що в основному у нас з друзями більше спільного ( і це логічно ).

І тільки рейтинг відмінності між Азарової і Петрової показує, що ці два моїх друга ( подруги ) найбільш різні в своїх інтересах, так як їх коеф. дорівнює 17.66, що більше середнього значення.

Замість висновку



Таким чином, даний спосіб можна використовувати для ранжирування користувачів по їх інтересам на сайтах знайомств. Ми бачимо, що чим більше питань, тим точніше порівняння особистостей. Створивши, припустимо, при реєстрації опитувальник з 100 питань і склавши карту для невеликої соціальної мережі ( так як обсяг пам'яті для цього методу зростає лінійно з приростом користувачів ) можна рекомендувати людей для спілкування/знайомства.

Повний код
#!/usr/bin/python

import sys
import pickle
import math

Dictionary = []
Points = {}
PSize = 0

def DictGen():
print("~~~~")
DictList = []
print("exit For enter a \"0\"")
while True:
s = str(input("> "))
if (s == "0"):
break;
else:
DictList.append(s)

print("Enter a name for new list of keys: ")
fname = str(input("> "))
with open(fname, 'wb') as f:
росол.dump(DictList, f)
print("Saved with name ", fname, sep = "")

def DictLoad():
print("~~~~")
fname = str(input("Enter a name of list to load\n> "))
with open(fname, 'rb') as f:
Словник.clear()
Словник.extend(pickle.load(f))

def NewPoint():
print("~~~~")
if (not Dictionary):
print("List of keys not loaded (command 2)")
else:
LocalPoint = {}
for key in Dictionary:
print(key, ": ", sep="", end="")
mark = float(input())
LocalPoint[key] = mark
print("Enter a name for new point: ")
fname = str(input("> "))
with open(fname, 'wb') as f:
росол.dump(LocalPoint, f)
print("New point saved with name ", fname, sep="")

def LoadPoint():
print("~~~~")
fname = str(input("Enter a name of point to load\n> "))
with open(fname, 'rb') as f:
LocalPoint = pickle.load(f)
Points[PSize] = LocalPoint

def calc(nPoint):
result = 0.0

print("sqrt(", end="")
for key in Dictionary:
print("(", Points[nPoint[0]][key], " - ", Points[nPoint[1]][key], ")^2 + ", sep="", end="")
result = result + math.pow((Points[nPoint[0]][key] - Points[nPoint[1]][key]),2)
print(")")
result = math.sqrt(result)
return result

def GenerateMap():
print("~~~~")
for i in range(1, PSize):
for j in range(i + 1, PSize + 1):
print(i, " and ", j, " = ", calc( (i, j) ), sep="")

while True:

print("0 - exit")
print("1 - generate a list of keys")
print("2 - load a map of marks") 
print("3 - add a new point in dimension") 
print("4 - load a new point in dimension") 
print("5 - calculate distance from two points of dimension") 
print("6 - print information")
print("7 - create a map with distance for every point")
i = int(input("#-> "))

if (i == 0):
sys.exit()
elif (i == 1):
DictGen()
elif (i == 2):
DictLoad()
elif (i == 3):
NewPoint()
elif (i == 4):
PSize = PSize + 1
LoadPoint()
elif (i == 5):
print("Enter a two numbers points of which you want to calculate a distance")
nPoint = tuple(int(x.strip()) for x in input().split(' '))
print("Difference: ", calc(nPoint), sep="")
input()
sys.exit()


elif (i == 6):
print("Dictionary", Dictionary, sep = ": ")
print("Points: ", Points, sep = ": ")
print("Total points: ", PSize, sep = "")

elif (i == 7):
GenerateMap()
else:
print("Unknown command")



sys.exit()



Джерело: Хабрахабр

0 коментарів

Тільки зареєстровані та авторизовані користувачі можуть залишати коментарі.