2017-08-28 1 views
2

J'ai un DataFrame à 2 colonnes, la colonne 1 correspond au client, la colonne 2 correspond à la ville que ce client a visité. La trame de données se présente comme suit:Pandas Python: comment convertir une liste de mappages de paires en format ligne-vecteur?

print(df) 

    customer visited_city 
0 John  London 
1 Mary  Melbourne 
2 Steve  Paris 
3 John  New_York 
4 Peter  New_York 
5 Mary  London 
6 John  Melbourne 
7 John  New_York 

Je voudrais convertir le dataframe ci-dessus dans un format vecteur-ligne, de sorte que chaque ligne représente un utilisateur unique avec le vecteur de ligne indiquant les villes visitées.

print(wide_format_df) 

      London Melbourne New_York Paris 
John  1.0  1.0  1.0  0.0 
Mary  1.0  1.0  0.0  0.0 
Steve  0.0  0.0  0.0  1.0 
Peter  0.0  0.0  1.0  0.0 

Voici le code que j'ai utilisé pour générer le format large. Il parcourt chaque utilisateur un par un. Je me demandais s'il y avait un moyen plus efficace de le faire?

import pandas as pd 
import numpy as np 

UNIQUE_CITIESS = np.sort(df['visited_city'].unique()) 
p = len(UNIQUE_CITIESS) 
unique_customers = df['customer'].unique().tolist() 

X = [] 
for customer in unique_customers: 
    x = np.zeros(p)  
    city_visited = np.sort(df[df['customer'] == customer]['visited_city'].unique()) 
    visited_idx = np.searchsorted(UNIQUE_CITIESS, city_visited) 
    x[visited_idx] = 1  
    X.append(x) 
wide_format_df = pd.DataFrame(np.array(X), columns=UNIQUE_CITIESS, index=unique_customers) 
wide_format_df 

Répondre

3

S'il vous plaît noter que votre question a été modifié de telle sorte que les réponses à condition de ne plus répondre ta question. Ils doivent s'ajuster pour ne retourner que 1 pour John en New York malgré le fait qu'il a été là deux fois.

Option 1 pir1
J'aime cette réponse parce que je pense qu'il est élégant.

pd.get_dummies(df.customer).T.dot(pd.get_dummies(df.visited_city)).clip(0, 1) 

     London Melbourne New_York Paris 
John  1   1   1  0 
Mary  1   1   0  0 
Peter  0   0   1  0 
Steve  0   0   0  1 

Option 2 pir2
Cette réponse devrait être rapide.

i, r = pd.factorize(df.customer.values) 
j, c = pd.factorize(df.visited_city.values) 
n, m = r.size, c.size 
b = np.zeros((n, m), dtype=int) 
b[i, j] = 1 

pd.DataFrame(b, r, c).sort_index().sort_index(1) 

     London Melbourne New_York Paris 
John  1   1   1  0 
Mary  1   1   0  0 
Peter  0   0   1  0 
Steve  0   0   0  1 

Option 3 pir3
pratique et assez rapide

df.groupby(['customer', 'visited_city']).size().unstack(fill_value=0).clip(0, 1) 

visited_city London Melbourne New_York Paris 
customer           
John    1   1   1  0 
Mary    1   1   0  0 
Peter    0   0   1  0 
Steve    0   0   0  1 

Timing
code ci-dessous

# Multiples of Minimum time 
# 
      pir1 pir2  pir3  wen  vai 
10  1.392237 1.0 1.521555 4.337469 5.569029 
30  1.445762 1.0 1.821047 5.977978 7.204843 
100 1.679956 1.0 1.901502 6.685429 7.296454 
300 1.568407 1.0 1.825047 5.556880 7.210672 
1000 1.622137 1.0 1.613983 5.815970 5.396008 
3000 1.808637 1.0 1.852953 4.159305 4.224724 
10000 1.654354 1.0 1.502092 3.145032 2.950560 
30000 1.555574 1.0 1.413612 2.404061 2.299856 

enter image description here

wen = lambda d: d.pivot_table(index='customer', columns='visited_city',aggfunc=len, fill_value=0) 
vai = lambda d: pd.crosstab(d.customer, d.visited_city) 
pir1 = lambda d: pd.get_dummies(d.customer).T.dot(pd.get_dummies(d.visited_city)).clip(0, 1) 
pir3 = lambda d: d.groupby(['customer', 'visited_city']).size().unstack(fill_value=0).clip(0, 1) 

def pir2(d): 
    i, r = pd.factorize(d.customer.values) 
    j, c = pd.factorize(d.visited_city.values) 
    n, m = r.size, c.size 
    b = np.zeros((n, m), dtype=int) 
    b[i, j] = 1 

    return pd.DataFrame(b, r, c).sort_index().sort_index(1) 

results = pd.DataFrame(
    index=[10, 30, 100, 300, 1000, 3000, 10000, 30000], 
    columns='pir1 pir2 pir3 wen vai'.split(), 
    dtype=float 
) 

for i in results.index: 
    d = pd.concat([df] * i, ignore_index=True) 
    for j in results.columns: 
     stmt = '{}(d)'.format(j) 
     setp = 'from __main__ import d, {}'.format(j) 
     results.at[i, j] = timeit(stmt, setp, number=10) 

print((lambda r: r.div(r.min(1), 0))(results)) 

results.plot(loglog=True) 
+0

J'ai manqué toute l'action sur celui-ci, l'utilisation intéressante de clip. +1 – Vaishali

+0

Merci @Vaishali – piRSquared

+0

réponse fantastique, merci @piRSquared !!! – cwl

3

Vous pouvez utiliser crosstab

pd.crosstab(df.customer, df.visited_city) 

Vous obtenez

visited_city London Melbourne New_York Paris 
customer     
John   1  1   1   0 
Mary   1  1   0   0 
Peter   0  0   1   0 
Steve   0  0   0   1 
+0

C'est une bonne idée, mais la question est que potentiellement une paire donnée pourrait apparaître plusieurs fois dans la trame de données d'origine, ce faisant 'crosstab' se traduit par un nombre au lieu de le vecteur indicateur. – cwl

+0

En fait, je pense qu'on peut utiliser 'df.drop_duplicates()' pour supprimer les lignes dupliquées dans le DataFrame original, donc 'crosstab' devrait être assez bon, merci @Vaishali! – cwl

+0

cwl, vous pouvez regarder @piRSquared réponse qui adresse les doublons et est plus efficace. – Vaishali

2

Ou vous pouvez en utilisant

df.pivot_table(index='customer', columns='visited_city',aggfunc=len, fill_value=0) 

visited_city London Melbourne New_York Paris 
customer           
John    1   1   1  0 
Mary    1   1   0  0 
Peter    0   0   1  0 
Steve    0   0   0  1