3
votes

Associer deux personnes en fonction des attributs

J'ai un dataframe avec différentes personnes. Chaque ligne contient des attributs qui caractérisent la personne individuelle. Fondamentalement, j'ai besoin de quelque chose comme un filtre ou un algorithme de correspondance qui pondère des attributs spécifiques. Le dataframe ressemble à ceci:

    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

Le dataframe df ressemble à ceci:

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],
})

Je recherche un algorithme qui regroupe toutes les personnes du dataframe par paires. Mon plan est de trouver des paires de deux personnes en fonction des attributs suivants:

  1. Une personne doit avoir une cuisine (cuisine = 1)
    Il est important qu'au moins une personne dispose d'une cuisine.

    cuisine = 0 -> personne n'a pas de cuisine

    cuisine = 1 -> personne a une cuisine

    cuisine = 2 -> personne a une cuisine mais seulement en cas d'urgence (quand il n'y a pas d'autre option)

  2. Mêmes préférences alimentaires

    food = 0 -> mangeur de viande

    food = 1 -> n'a pas d'importance

    nourriture = 2 -> vegan

    nourriture = 3 -> végétarienne

    Un mangeur de viande (nourriture = 0) peut être jumelé avec une personne qui ne se soucie pas des préférences alimentaires (nourriture = 1) mais ne peut pas être jumelé avec un végétalien ou végétarien. Un végétalien (nourriture = 2) correspond le mieux à un végétarien (nourriture = 3) et, si nécessaire, peut aller avec de la nourriture = 1. Et ainsi de suite ...

  3. Âge similaire

    Il y a neuf groupes d'âge: 10-18 ans; 18-22; 22-26; 26 à 29, 29 à 34; 34-40; 40-45; 45-55 et 55-75. Les personnes du même groupe d'âge correspondent parfaitement. Les jeunes groupes d'âge avec les groupes plus âgés ne correspondent pas très bien. Des groupes d'âge similaires correspondent un peu mieux. Il n'y a pas de condition clairement définie. La signification de «vieux» et «jeune» est relative.

Le sexe n'a pas d'importance. Il existe de nombreuses combinaisons de paires possibles. Étant donné que ma base de données réelle est très longue (3000 lignes), je dois trouver une solution automatisée. Une solution qui me donne les meilleures paires dans un dataframe ou un dictionnaire ou autre chose.

Je ne sais vraiment pas comment aborder ce problème. Je cherchais des problèmes similaires sur Stack Overflow, mais je n'ai rien trouvé de convenable. C'était surtout trop théorique. De plus, je n'ai rien trouvé qui correspond vraiment à mon problème.

Ma sortie attendue ici serait, par exemple, un dictionnaire (je ne sais pas comment) ou un dataframe qui est trié de manière à ce que toutes les deux lignes puissent être vu comme une paire.

Contexte: Le but est de faire des paires pour des activités de temps libre. Par conséquent, je pense que les personnes appartenant à des groupes d'âge identiques ou similaires partagent le même intérêt, c'est pourquoi je veux tenir compte de ce fait dans mon code.


3 commentaires

@PParker En référence à votre déclaration "Si possible, les paires sont dans le même groupe d'âge. Si ce n'est pas possible, alors peut-être dans un groupe d'âge similaire." Avez-vous un âge numérique dans la colonne d'âge ou seulement deux valeurs de chaîne: «Jeune» et «Vieux»?


@PParker Vous pouvez consulter les ressources suivantes: Maximiser les appariements sous réserve de contrainte de distance , Algorithme Blossom , Correspondance avec les contraintes , Problème d'attribution , algorithme hongrois .


@AnidhyaBhatnagar Dans mon exemple simplifié, je n'ai que deux groupes d'âge (jeunes et vieux). Cependant, dans la base de données réelle, j'ai un âge numérique. Mon projet est de créer plusieurs tranches d'âge (par exemple "très jeune", "jeune", "vieux", "très vieux", ...).


3 Réponses :


1
votes

Cela me semble être un problème très intéressant. Il existe plusieurs façons de résoudre ce problème. Je vous en indiquerai une, mais je vous relierai à une autre solution qui, selon moi, est en quelque sorte liée.

Une approche possible peut être de créer une colonne supplémentaire dans votre cadre de données, y compris un "code" qui fait référence aux attributs donnés. Par exemple:

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]

Ceci " code 'est composé de courts métrages de vos attributs. Puisque le sexe n'a pas d'importance, le premier signe dans le code représente la «nourriture», le deuxième pour «l'âge» et le troisième pour la «cuisine».

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

Sur la base de ces codes, vous pouvez créer un modèle. Je vous recommande de travailler avec Expressions régulières pour cela. Vous pouvez ensuite écrire quelque chose comme ceci:

    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

Sur cette base, vous pouvez boucler sur les entrées et les assembler comme vous le souhaitez. Il y a peut-être une solution beaucoup plus simple et je n'ai pas vérifié le code, mais cela m'est venu à l'esprit. Une chose est sûre: Utilisez des expressions régulières pour la correspondance.


0 commentaires

1
votes

Eh bien, testons la cuisine.

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)

Bon maintenant que nous avons trouvé une cuisine chez les gens qui ont une maison de cuisine, trouvons les gens sans cuisine quelqu'un avec une nourriture similaire préférences. Créons une variable qui nous indique combien de personnes ont une cuisine (x). Faisons également la variable person pour compter les personnes.

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

Je travaille actuellement sur la partie âge en ajustant quelque chose


0 commentaires

7
votes

J'ai fait un ajout en mettant 'nom' comme clé pour identifier la personne.

Approche

L'approche est que j'ai noté les valeurs qui sont ensuite utilisé pour filtrer les paires finales selon les conditions données.

Notation pour la cuisine

Pour les scores de cuisine, nous avons utilisé:

  • La personne n'a pas de cuisine: 0
  • La personne a une cuisine: 1
  • La personne dispose d'une cuisine mais uniquement en cas d'urgence: 0,5

si Condition Logic pour la cuisine

Nous vérifions que si [score cuisine de l'enregistrement 1] + [score de la cuisine de l'enregistrement 2] est supérieur que zéro . Comme les cas suivants seront là:

  1. Les deux membres n'ont pas de cuisine (la somme sera de 0) [EXCLU avec> 0 condition]
  2. Les deux membres ont une cuisine (la somme sera de 2)
  3. Un membre a une cuisine et un autre n’a pas de cuisine (la somme sera de 1)
  4. Les deux ont une cuisine d'urgence (la somme sera de 1)
  5. L'un a une cuisine d'urgence et l'autre une cuisine (la somme sera de 1,5)
  6. Un membre dispose d'une cuisine d'urgence et un autre n'a pas de cuisine (la somme sera de 0,5)

Notation pour la nourriture

Pour les notes alimentaires, nous avons utilisé:

  • food = 0 -> mangeur de viande: -1
  • food = 1 -> n'a pas d'importance: 0
  • nourriture = 2 -> végétalien: 1
  • food = 3 -> végétarien: 1

if Condition Logic for Food

Nous vérifions si * [score alimentaire de l'enregistrement 1] * [score alimentaire de l'enregistrement 2] * est supérieur ou égal fort > à Zéro . Comme les cas suivants seront là:

  1. Les deux membres sont des mangeurs de viande: -1 x -1 = 1 [INCLUS]
  2. L'un des membres est un mangeur de viande et un autre végétalien ou végétarien: -1 x 1 = -1 [EXCLUS]
  3. L'un des membres est un mangeur de viande et l'autre n'a pas d'importance: -1 x 0 = 0 [INCLUS]
  4. L'un des membres est végétalien ou végétarien et l'autre n'a pas d'importance: 1 x 0 = 0 [INCLUS]
  5. Les deux membres sont soit végétaliens, soit végétariens: 1 x 1 = 1 [INCLUS]

Notation pour les groupes d'âge

Pour la notation des groupes d'âge, nous avons attribué certaines valeurs aux groupes comme suit:

  • 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

Calcul du score d'âge

Pour calculer le score d'âge, la formule suivante a été utilisée: age_score = round ((1 - (abs (Age Group Value Person 1 - Age Group Value of Person 2) / 10)), 2)

Dans la formule ci-dessus, nous calculons a été fait comme suit:

  1. Nous avons d'abord calculé la valeur absolue de la différence entre les valeurs des tranches d'âge des deux personnes.
  2. Ensuite, nous le divisons par 10 pour le normaliser.
  3. En outre, nous avons soustrait cette valeur de 1 pour inverser la distance, donc après cette étape, nous avons une valeur plus élevée pour les personnes de groupes d'âge similaires ou plus proches et une valeur inférieure pour les personnes de groupes d'âge différents ou plus éloignés.

Les cas seront comme:

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

Calcul du score final

Pour calculer le score final, nous avons utilisé:

Score final = score alimentaire + score cuisine + score âge

Ensuite, nous avons trié les données sur le score final pour obtenir les meilleures paires.

Code de solution

             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
..         ...        ...  ...

DataFrame d'entrée / intermédiaire avec scores

         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

Output

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)

Cette solution a une portée supplémentaire d'optimisation.


9 commentaires

Merci beaucoup pour cette excellente approche de mon problème et désolé pour ma réponse tardive. J'ai une autre question que j'aimerais que vous posiez: 1) Disons que j'ai plusieurs groupes d'âge. Dans mon cas, j'ai 9 groupes d'âge. Je suppose que je dois changer l'équation. Cependant, je ne sais pas quel score je dois attribuer à chaque tranche d'âge. Peut-être pouvez-vous m'aider ici!


@PParker Merci pour la réponse. Veuillez accepter comme bonne réponse si cela a fonctionné pour vous. Pour plusieurs groupes d'âge, j'ai besoin de connaître la relation entre ces neuf groupes d'âge et leurs priorités de correspondance pour définir l'équation. Je vais essayer.


Merci de votre aide! J'ai neuf groupes d'âge: 10-18 ans; 18-22; 22-26; 26 à 29, 29 à 34; 34-40; 40-45; 45-55 et 55-75. Les personnes du même groupe d'âge correspondent parfaitement. Les jeunes groupes d'âge avec les groupes plus âgés ne correspondent pas très bien. Des groupes d'âge similaires correspondent un peu mieux. Il n'y a pas de condition clairement définie. La signification de «vieux» et «jeune» est relative. Contexte: Le but est de former des paires pour des activités de temps libre. Par conséquent, je pense que les personnes appartenant à des groupes d'âge identiques ou similaires partagent le même intérêt, c'est pourquoi je veux tenir compte de ce fait dans mon code.


Je vais essayer si nous pouvons utiliser une sorte de score de distance entre les groupes. Plus le groupe est éloigné, moins le score est élevé. Mais il faut voir qu'en même temps, cela ne devrait pas faire de biais de score global ou remplacer l'effet d'autres propriétés. Laissez-moi penser à une solution.


@PParker J'ai mis à jour la question avec les informations supplémentaires que vous avez fournies sur les groupes d'âge et mis à jour la réponse et son explication également. Maintenant, le programme considère la distance entre les deux groupes d'âge des personnes et les note ensuite sur la base de leur distance. J'espère que cela t'aides.


@ AnidhyaBhatnagar Merci beaucoup pour cette solution extraordinaire. Cela fonctionne très bien. Merci également pour vos explications détaillées. Permettez-moi de vous poser quelques questions plus générales: 1.) Est-il correct de dire que (selon les personnes) tout le monde n'a pas de partenaire? 2.) En fonction du score, comment choisiriez-vous le partenaire réel? Je suis confus, car par exemple sabastein a le score le plus élevé avec smith (4,0) mais smith a le score le plus élevé avec jim (3,4). Peut-être existe-t-il un moyen explicite de sélectionner les paires dans la trame de données finale?


@PParker 1.) Oui, il est correct de dire que selon les personnes et leurs caractéristiques, tout le monde n'aura pas de partenaire. 2.) Ne vous méprenez pas, Sabastein a le score le plus élevé avec Smith et le Smith a le score le plus élevé avec Sabastein. Parce que si vous correspondez à Sabastein -> Smith ou Smith -> Sabastein, le score sera le même 4.0. Ainsi, lorsque vous choisissez une paire, dites (Sabastein & Smith), ignorez toutes les autres paires qui ont Sabastein ou Smith. Ainsi, vous en tirerez des paires uniques.


Merci beaucoup pour votre réponse. Je viens de réaliser qc. nouveau: nous avons maintenant cette trame de données très longue avec différentes combinaisons de paires et scores. Il s'avère qu'une personne peut être mieux jumelée à plusieurs personnes. Par exemple, Anna travaille mieux avec Katie (3,3), mais Christina travaille également mieux avec Katie (2,4). Il existe de nombreux exemples comme celui-ci. Je suppose donc que la meilleure approche est de créer une fonction qui sélectionne les meilleures paires de la trame de données. Le but est de trouver autant de bonnes paires que possible. Vous pouvez peut-être commenter cela. Comment aborderiez-vous cela en général?


@PParker Oui! le but ultime est de faire correspondre et de créer les meilleures paires. Eh bien, c'est une question délicate. Penser à cela nécessite un certain temps et travailler avec un échantillon pour arriver à la meilleure solution. (comme supposons que A et B correspondent le mieux, mais A et C, B et D ont une certaine correspondance, mais C et D ne correspondent certainement pas, ici, il serait préférable que A et C correspondent B et D correspondent.) I Je vais un peu occupé maintenant quelques jours - je le regarderai le week-end prochain. Jusque-là, vous pouvez essayer de retirer une paire, puis de supprimer tous les enregistrements suivants avec l'un ou l'autre de n'importe quel côté, puis d'en choisir un unique et de le défausser, etc.