根据属性将两个人配对在一起

时间:2019-01-01 14:52:20

标签: python pandas algorithm dataframe match

我有一个与其他人在一起的数据框。每行都包含表征个人的属性。基本上,我需要权重特定属性的过滤器或匹配算法之类的东西。数据框如下所示:

df= pd.DataFrame({
'sex' : [m,f,m,f,m,f],
'food' : [0,0,1,3,4,3],
 'age': [young, young, young, old, young, young]
'kitchen': [0,1,2,0,1,2],
})

数据帧df如下所示:

    sex food  age     kitchen
0   m    0    young    0
1   f    0    young    1
2   m    1    young    2
3   f    3    old      0
4   m    4    young    1
5   f    3    young    2

我正在寻找一种算法,该算法将数据帧中的所有人分组。我的计划是根据以下属性找到两个人对:

  1. 一个人必须有厨房(厨房= 1)
    至少要有一个厨房是很重要的。

    厨房= 0->人没有厨房

    厨房= 1->人有厨房

    kitchen = 2->人有厨房,但只有在紧急情况下(没有其他选择时)

  2. 相同的食物偏好

    food = 0->食肉者

    food = 1->没关系

    food = 2->素食主义者

    food = 3->素食主义者

    食肉者(食物= 0)可以与不关心食物偏好(食物= 1)但不能与素食主义者或素食者相匹配的人搭配。素食主义者(食物= 2)最适合素食主义者(食物= 3),并且在必要时可以搭配食物= 1。等等...

  3. 类似年龄

    共有9个年龄段:10-18岁; 18-22; 22-26; 26-29、29-34; 34-40; 40-45; 45-55和55-75。同一年龄段的人匹配得很好。年轻的年龄段与年龄较大的年龄段的匹配度不是很好。相似年龄段的人匹配得更好。没有明确定义的条件。 “老”和“年轻”的含义是相对的。

性别无关紧要。有许多可能的配对组合。因为我的实际数据帧很长(3000行),所以我需要找到一个自动化的解决方案。一种解决方案,可以为我提供数据框或字典或其他内容中的最佳配对。

我真的不知道该如何解决这个问题。我一直在寻找Stack Overflow上的类似问题,但没有找到合适的方法。在大多数情况下,这只是理论上的问题。另外,我找不到任何适合我问题的东西。

我在这里的预期输出将是例如字典(不确定如何)或数据帧,其排序方式是每两行可以看作一对。

背景:目标是为一些业余时间活动配对。因此,我认为处于相同或相似年龄段的人们具有相同的兴趣,因此我想在我的代码中考虑这个事实。

3 个答案:

答案 0 :(得分:4)

我已经通过添加'name'作为识别该人的密钥来进行加法。

方法

该方法是我对值进行了评分,该值进一步用于根据给定条件过滤最终对。

厨房打分

我们使用的厨房评分:

  • 人员没有厨房:0
  • 人有厨房:1
  • 人员有厨房,但只有紧急情况:0.5

如果厨房条件逻辑

我们检查 [记录1的厨房分数] + [记录2的厨房分数] 是否大于零。由于将出现以下情况:

  1. 两个成员都没有厨房(总和为0) [排除条件> 0]
  2. 两个成员都有厨房(总和为2)
  3. 一个成员有厨房,其他成员没有厨房(总和为1)
  4. 两个都有紧急厨房(总和为1)
  5. 一个有紧急厨房,另一个有厨房(总和为1.5)
  6. 一个成员拥有急救厨房,其他成员则没有厨房(总和为0.5)

食物评分

我们使用的食物评分:

  • food = 0->肉食者:-1
  • food = 1->没关系:0
  • food = 2->素食主义者:1
  • 食物= 3->素食主义者:1

如果食品有条件逻辑

我们检查* [记录1的食物得分] * [记录2的食物得分] *是否大于等于。由于将出现以下情况:

  1. 两个成员都是肉食者:-1 x -1 = 1 [已加入]
  2. 成员之一是食肉者和其他素食者:-1 x 1 = -1 [已排除]
  3. 成员之一是食肉者,其他无关紧要:-1 x 0 = 0 [已加入]
  4. 其中一位成员是素食主义者或素食主义者,而其他无关紧要:1 x 0 = 0 [已加入]
  5. 这两个成员都是素食主义者:1 x 1 = 1 [已加入]

按年龄段评分

对于得分年龄组,我们为这些组分配了一些值:

  • 10-18:1
  • 18-22:2
  • 22-26:3
  • 26-29:4
  • 29-34:5
  • 34-40:6
  • 40-45:7
  • 45-55:8
  • 55-75:9

年龄得分计算

为了计算年龄得分,使用了以下公式: age_score = round((1 - (abs(Age Group Value Person 1 - Age Group Value of Person 2) / 10)), 2)

在上面的公式中,我们的计算如下:

  1. 首先,我们计算了两个年龄段的值之间的差的绝对值。
  2. 然后我们将其除以10进行归一化。
  3. 我们进一步从1中减去此值以求距离的倒数,因此在此步骤之后,我们对相似​​或相近年龄组的人具有较高的价值,而对不同或相近年龄组的人具有较低的价值。

情况如下:

  1. 18-22和18-22: round(1 - (abs(2 - 2) / 10), 2) = 1.0
  2. 45-55和45-55: round(1 - (abs(8 - 8) / 10), 2) = 1.0
  3. 18-22和45-55: round(1 - (abs(2 - 8) / 10), 2) = 0.4
  4. 10-18和55-75: round(1 - (abs(1 - 9) / 10), 2) = 0.2

最终成绩计算

我们使用以下方法计算最终得分:

Final Score = Food Score + Kitchen Score + Age Score

然后,我们对最终得分进行了排序,以获得最佳配对。

解决方案代码

import pandas as pd
import numpy as np

# Creating the DataFrame, here I have added the attribute 'name' for identifying the record.
df = pd.DataFrame({
    'name' : ['jacob', 'mary', 'rick', 'emily', 'sabastein', 'anna', 
              'christina', 'allen', 'jolly', 'rock', 'smith', 'waterman', 
              'mimi', 'katie', 'john', 'rose', 'leonardo', 'cinthy', 'jim', 
              'paul'],
    'sex' : ['m', 'f', 'm', 'f', 'm', 'f', 'f', 'm', 'f', 'm', 'm', 'm', 'f', 
             'f', 'm', 'f', 'm', 'f', 'm', 'm'],
    'food' : [0, 0, 1, 3, 2, 3, 1, 0, 0, 3, 3, 2, 1, 2, 1, 0, 1, 0, 3, 1],
    'age' : ['10-18', '22-26', '29-34', '40-45', '18-22', '34-40', '55-75',
             '45-55', '26-29', '26-29', '18-22', '55-75', '22-26', '45-55', 
             '10-18', '22-26', '40-45', '45-55', '10-18', '29-34'],
    'kitchen' : [0, 1, 2, 0, 1, 2, 2, 1, 0, 0, 1, 0, 1, 1, 1, 0, 2, 0, 2, 1],
})

# Adding a normalized field 'k_scr' for kitchen
df['k_scr'] = np.where((df['kitchen'] == 2), 0.5, df['kitchen'])

# Adding a normalized field 'f_scr' for food
df['f_scr'] = np.where((df['food'] == 1), 0, df['food'])
df['f_scr'] = np.where((df['food'] == 0), -1, df['f_scr'])
df['f_scr'] = np.where((df['food'] == 2), 1, df['f_scr'])
df['f_scr'] = np.where((df['food'] == 3), 1, df['f_scr'])

# Adding a normalized field 'a_scr' for age
df['a_scr'] = np.where((df['age'] == '10-18'), 1, df['age'])
df['a_scr'] = np.where((df['age'] == '18-22'), 2, df['a_scr'])
df['a_scr'] = np.where((df['age'] == '22-26'), 3, df['a_scr'])
df['a_scr'] = np.where((df['age'] == '26-29'), 4, df['a_scr'])
df['a_scr'] = np.where((df['age'] == '29-34'), 5, df['a_scr'])
df['a_scr'] = np.where((df['age'] == '34-40'), 6, df['a_scr'])
df['a_scr'] = np.where((df['age'] == '40-45'), 7, df['a_scr'])
df['a_scr'] = np.where((df['age'] == '45-55'), 8, df['a_scr'])
df['a_scr'] = np.where((df['age'] == '55-75'), 9, df['a_scr'])

# Printing DataFrame after adding normalized score values
print(df)

commonarr = [] # Empty array for our output
dfarr = np.array(df) # Converting DataFrame to Numpy Array
for i in range(len(dfarr) - 1): # Iterating the Array row
    for j in range(i + 1, len(dfarr)): # Iterating the Array row + 1
        # Check for Food Condition to include relevant records
        if dfarr[i][6] * dfarr[j][6] >= 0: 
            # Check for Kitchen Condition to include relevant records
            if dfarr[i][5] + dfarr[j][5] > 0:
                row = []
                # Appending the names
                row.append(dfarr[i][0])
                row.append(dfarr[j][0])
                # Appending the final score
                row.append((dfarr[i][6] * dfarr[j][6]) +
                           (dfarr[i][5] + dfarr[j][5]) +
                           (round((1 - (abs(dfarr[i][7] -
                                            dfarr[j][7]) / 10)), 2)))

                # Appending the row to the Final Array
                commonarr.append(row)

# Converting Array to DataFrame
ndf = pd.DataFrame(commonarr)

# Sorting the DataFrame on Final Score
ndf = ndf.sort_values(by=[2], ascending=False)
print(ndf)

具有得分的输入/中间数据框

         name sex  food    age  kitchen  k_scr  f_scr a_scr
0       jacob   m     0  10-18        0    0.0     -1     1
1        mary   f     0  22-26        1    1.0     -1     3
2        rick   m     1  29-34        2    0.5      0     5
3       emily   f     3  40-45        0    0.0      1     7
4   sabastein   m     2  18-22        1    1.0      1     2
5        anna   f     3  34-40        2    0.5      1     6
6   christina   f     1  55-75        2    0.5      0     9
7       allen   m     0  45-55        1    1.0     -1     8
8       jolly   f     0  26-29        0    0.0     -1     4
9        rock   m     3  26-29        0    0.0      1     4
10      smith   m     3  18-22        1    1.0      1     2
11   waterman   m     2  55-75        0    0.0      1     9
12       mimi   f     1  22-26        1    1.0      0     3
13      katie   f     2  45-55        1    1.0      1     8
14       john   m     1  10-18        1    1.0      0     1
15       rose   f     0  22-26        0    0.0     -1     3
16   leonardo   m     1  40-45        2    0.5      0     7
17     cinthy   f     0  45-55        0    0.0     -1     8
18        jim   m     3  10-18        2    0.5      1     1
19       paul   m     1  29-34        1    1.0      0     5

输出

             0          1    2
48   sabastein      smith  4.0
10        mary      allen  3.5
51   sabastein      katie  3.4
102      smith        jim  3.4
54   sabastein        jim  3.4
99       smith      katie  3.4
61        anna      katie  3.3
45   sabastein       anna  3.1
58        anna      smith  3.1
14        mary       rose  3.0
12        mary       mimi  3.0
84       allen     cinthy  3.0
98       smith       mimi  2.9
105   waterman      katie  2.9
11        mary      jolly  2.9
50   sabastein       mimi  2.9
40       emily      katie  2.9
52   sabastein       john  2.9
100      smith       john  2.9
90        rock      smith  2.8
47   sabastein       rock  2.8
0        jacob       mary  2.8
17        mary       paul  2.8
13        mary       john  2.8
119      katie        jim  2.8
116       mimi       paul  2.8
111       mimi       john  2.8
103      smith       paul  2.7
85       allen       paul  2.7
120      katie       paul  2.7
..         ...        ...  ...

此解决方案具有进一步的优化范围。

答案 1 :(得分:1)

对我来说,这似乎是一个非常有趣的问题。有几种方法可以解决此问题。我将向您声明一个,但会将您链接到我认为与之相关的another solution

一种可能的方法是在数据框中创建一个附加列,包括一个引用给定属性的“代码”。例如:

    sex  food  age      kitchen   code
0   m    0     young    0         0y0
1   f    0     young    1         0y1
2   m    1     young    2         1y2
3   f    3     old      0         3o0
4   m    4     young    1         4y1
5   f    3     young    2         3y2

此“代码”由您属性的缩写组成。由于性别无关紧要,因此代码中的第一个符号代表“食物”,第二个符号代表“年龄”,第三个符号代表“厨房”。

4y1 = food 4, age young, kitchen 1.

根据这些代码,您可以提出一个模式。我建议您为此使用Regular Expressions。然后,您可以编写如下内容:

import re
haskitchen = r'(\S\S1)
hasnokitchen = r'(\S\S0)
df_dict = df.to_dict

match_kitchen = re.findall(haskitchen, df_dict)
match_nokitchen = re.dinfall(hasnokitchen, df_dict)

kitchendict["Has kitchen"] = [match_kitchen]
kitchendict["Has no kitchen"] = [match_notkitchen]

基于此,您可以遍历条目并将其按需要组合在一起。可能有一个更简单的解决方案,而我没有证明代码,但这只是我的想法。可以肯定的是:使用正则表达式进行匹配。

答案 2 :(得分:1)

好吧,我们来测试一下厨房。

for I in(kitchen):
    if (I != 0):
        print("Kitchen Found)
    else:
        print("No kitchen")

好吧,现在我们已经在拥有厨房房屋的人中找到了厨房,让我们找到没有厨房的人,他们的食物偏好相似。让我们创建一个变量,告诉我们有多少人拥有厨房(x)。让我们也将人员变量设为计数人员。

people = 0
x = 0
for I in(kitchen):
    x = x + 1
    for A in (food):
            if (I != 0):
                x = x + 1
                print("Kitchen Found)
            else:
                print("No kitchen")
                for J in(food):
                    if(i == J):
                        print("food match found")
                    elif(A == 0):
                        if(J == 1):
                            print("food match found for person" + x)
                    elif(A == 2 or A == 3):
                        if(J == 2 or J == 3 or J == 1):
                            print("food match found for person" + x)

我目前正在调整年龄部分