2017-08-23 1 views
1

Il y a une opération qui est un peu contre-intuitive quand on utilise la méthode apply() de pandas. Il m'a fallu quelques heures de lecture pour résoudre, alors voilà.À partir d'une base de données utilisant la méthode apply(), comment retourner une nouvelle colonne avec des listes d'éléments de la base de données?

Voici donc ce que j'essayais d'accomplir.

J'ai un dataframe de pandas géants comme ceci:

test = pd.DataFrame({'one': [[2],['test']], 'two': [[5],[10]]}) 
     one two 
0  [2] [5] 
1 [test] [10] 

et je veux ajouter les colonnes par ligne pour créer une liste résultante de longueur = à la longueur initiale du dataframe comme ceci:

def combine(row): 
    result = row['one'] + row['two'] 
    return(result) 

Lors de l'exécution à travers la trame de données en utilisant la méthode appliquer():

test.apply(lambda x: combine(x), axis=1) 
    one two 
0  2 5 
1 test 10 

Quelle st Ce n'est pas tout à fait ce que nous voulions. Ce que nous voulons est:

 result 
0  [2, 5] 
1 [test, 10] 

EDIT

Je sais qu'il ya des solutions plus simples à cet exemple. Mais ceci est une abstraction d'un operation.Here beaucoup plus complexe est un exemple de plus complexe:

df_one:

org_id  date  status  id 
0  2  2015/02/01  True  3 
1  10 2015/05/01  True  27 
2  10 2015/06/01  True  18 
3  10 2015/04/01  False  27 
4  10 2015/03/01  True  40 

df_two:

org_id  date 
0  12  2015/04/01 
1  10  2015/02/01 
2  2  2015/08/01 
3  10  2015/08/01 

Voilà une opération plus complexe:

def operation(row, df_one): 
    sel = (df_one.date < pd.Timestamp(row['date'])) & \ 
      (df_one['org_id'] == row['org_id']) 
    last_changes = df_one[sel].groupby(['org_id', 'id']).last() 
    id_list = last_changes[last_changes.status].reset_index().id.tolist() 

    return (id_list) 

puis finalement exécuter:

df_one.sort_values('date', inplace=True) 

    df_two['id_list'] = df_two.apply(
     operation, 
     axis=1, 
     args=(df_one,) 
    ) 

Cela serait impossible avec des solutions plus simples. D'où ma proposition on pourrait être à nouveau ci-dessous écrire operation à:

def operation(row, df_one): 
    sel = (df_one.date < pd.Timestamp(row['date'])) & \ 
      (df_one['org_id'] == row['org_id']) 
    last_changes = df_one[sel].groupby(['org_id', 'id']).last() 
    id_list = last_changes[last_changes.status].reset_index().id.tolist() 

    return pd.Series({'id_list': id_list}) 

Nous attendrions le résultat suivant:

id_list 
0  [] 
1  [] 
2  [3]  
3  [27,18,40] 
+0

Quel est le type de vos colonnes de date dans vos deux dataframes? Par exemple. string, timestamp, etc. – Alexander

+1

La dernière rangée ne devrait-elle pas être '[18, 27, 40]'? – Alexander

+1

ou '[27, 18, 40]' puisque la ligne 1 dans 'df_one' semble satisfaire aux critères et vient avant la ligne 2. – Alexander

Répondre

0

La réponse à ce problème réside dans la façon dont fonctionne la méthode pandas.apply().

Lors de la définition

def combine(row): 
    result = row['one'] + row['two'] 
    return(result) 

la fonction sera de retour une liste pour chaque ligne qui est passé. C'est un problème si nous utilisons la fonction avec la méthode .apply() car elle interprétera les listes dressées comme Série où chaque élément est une colonne de cette même ligne.

Pour résoudre cela, nous devons créer une série où nous spécifions un nouveau nom de colonne comme ceci:

def combine(row): 
    result = row['one'] + row['two'] 
    return pd.Series({'result': result}) 

Et si nous courons à nouveau:

test.apply(lambda x: combine(x), axis=1) 
     result 
0  [2, 5] 
1 [test, 10] 

Nous allons obtenir ce que nous initialement voulu! Encore une fois, c'est parce que nous obligeons les pandas à interpréter le résultat entier comme une colonne.

2

IIUC nous pouvons simplement résumer deux colonnes:

In [93]: test.sum(axis=1).to_frame('result') 
Out[93]: 
     result 
0  [2, 5] 
1 [test, 10] 

parce que quand on fait la somme des listes:

In [94]: [2] + [5] 
Out[94]: [2, 5] 

ils se concaténés ...

+0

c'est vrai. Mais mon exemple est une abtraction d'une opération beaucoup plus complexe qui ne serait pas possible avec votre solution. Ce serait cependant possible avec ma solution. – Nico