2016-01-30 4 views
0

J'ai plusieurs trames de données que j'essaie de découper et de réaffecter aux noms d'origine. Mais je constate qu'il y a un problème d'espace de noms. Voici ce que j'ai.Trame par programme une trame de données Pandas en place

import pandas as pd 
import numpy as np 

df_a = pd.DataFrame(np.random.rand(14,2), columns = list('XY')) 
df_b = pd.DataFrame(np.random.rand(14,2), columns = list('XY')) 

mylist =[df_a, df_b] 

def truncate_before(list_of_dfts, idx): 
    for dfts in list_of_dfts: 
     dfts = dfts[idx:] 
     print(dfts.head) 

truncate_before(mylist, 11) 
print(df_a) 

Dans les déclarations d'impression dans la fonction truncate_before, il montre 3 rangées, correspondant au 11e, 12e et 13e rang. Mais l'instruction d'impression finale montre les lignes 0 à 13. Donc en dehors de la fonction, il revient aux données originales.

J'avais l'impression que Python passait les arguments par référence. Qu'est-ce que je rate?

Répondre

0

En truncate_before:

def truncate_before(list_of_dfts, idx): 
    for dfts in list_of_dfts: 
     dfts = dfts[idx:] 
     print(dfts.head) 

le for-loop crée une variable locale dfts qui fait référence à la DataFrames dans list_of_dfts. Mais

 dfts = dfts[idx:] 

réaffecte une nouvelle valeur à dfts. Il ne modifie pas le DataFrame dans list_of_dfts.

Voir Facts and myths about Python names and values pour une excellente explication de la manière dont les noms de variables se lient aux valeurs, et quelles opérations modifient les valeurs par rapport aux nouvelles valeurs de liaison aux noms de variables.

Voici un certain nombre d'alternatives:

Modifier la liste inplace

def truncate_before(list_of_dfts, idx): 
    list_of_dfts[:] = [dfts[idx:] for dfts in list_of_dfts] 
    for dfts in list_of_dfts: 
     print(dfts.head) 

depuis l'attribution à list_of_dfts[:] (qui appelle list_of_dfts.__setitem__) modifie le contenu de list_of_dfts enplace.


import numpy as np 
import pandas as pd 

df_a = pd.DataFrame(np.random.rand(14,2), columns = list('XY')) 
df_b = pd.DataFrame(np.random.rand(14,2), columns = list('XY')) 

mylist = [df_a, df_b] 

def truncate_before(list_of_dfts, idx): 
    list_of_dfts[:] = [dfts[idx:] for dfts in list_of_dfts] 

print(mylist[0]) 
truncate_before(mylist, 11) 
print(mylist[0]) 

montre mylist[0] a été tronquée. Notez cependant que df_a fait toujours référence au DataFrame d'origine.


Retour la liste et réassigner mylist ou df_a, df_b au résultat

L'utilisation de valeurs de retour peut rendre inutile de modifier mylist en place.

À remaniera les variables globales df_a, df_b à une nouvelle valeur, vous pouvez faire truncate_before retour la liste des DataFrames et réattribuer df_a et df_b à la valeur retournée:

def truncate_before(list_of_dfts, idx): 
    return [dfts[idx:] for dfts in list_of_dfts] 

mylist = truncate_before(mylist, 11) # or 
# df_a, df_b = truncate_before(mylist, 11) # or 
# mylist = df_a, df_b = truncate_before(mylist, 11) 

Mais attention qu'il n'est probablement pas bon d'accéder aux DataFrames à travers les deux mylist et df_a et df_b, puisque comme l'exemple ci-dessus le montre, les valeurs ne restent pas coordonnées automatiquement magiquement.En utilisant mylist devrait suffire.


utiliser une méthode de trame de données avec le paramètre inplace, comme df.drop

dfts.drop (avec inplace=True) modifie dfts lui-même:

import numpy as np 
import pandas as pd 

df_a = pd.DataFrame(np.random.rand(14,2), columns = list('XY')) 
df_b = pd.DataFrame(np.random.rand(14,2), columns = list('XY')) 

mylist = [df_a, df_b] 

def truncate_before(list_of_dfts, idx): 
    for dfts in list_of_dfts: 
     dfts.drop(dfts.index[:idx], inplace=True) 

truncate_before(mylist, 11) 
print(mylist[0]) 
print(df_a) 

En modifiant dfts inplace, à la fois les valeurs de mylistetdf_a et df_b sont modifiés en même temps.

Notez que dfts.drop supprime des lignes en fonction de la valeur d'étiquette d'index. Donc, ce qui précède repose sur (suppose) que dfts.index est unique. Si dfts.index n'est pas unique, dfts.drop peut contenir plus de lignes que idx lignes. Par exemple,

df = pd.DataFrame([1,2], index=['A', 'A']) 
df.drop(['A'], inplace=True) 

gouttes les deux rangées faisant df un vide dataframe.

Notez également cet avertissement développeur de base de Pandas concernant l'utilisation de inplace:

Mon opinion personnelle: Je ne jamais utiliser les opérations en place. La syntaxe est plus difficile à read et n'offre aucun avantage.

Ceci est probablement dû sous le capot, dfts.drop crée une nouvelle trame de données et appelle ensuite la méthode privée _update_inplace pour affecter les nouvelles données à l'ancienne dataframe :

def _update_inplace(self, result, verify_is_copy=True): 
    """ 
    replace self internals with result. 
    ... 
    """ 
    self._reset_cache() 
    self._clear_item_cache() 
    self._data = getattr(result,'_data',result) 
    self._maybe_update_cacher(verify_is_copy=verify_is_copy) 

Depuis le result temporaire avait À créer de toute façon, il n'y a pas de bénéfice en termes de mémoire ou de performance des opérations «sur place» par rapport à une simple réaffectation.

+0

Ok, mylist était une construction pour regrouper toutes les données individuelles. N'y a-t-il rien d'autre que je puisse faire en ce qui concerne les bases de données originales? Bien sûr, je pourrais juste le faire un par un. df_a = df_a [idx:], etc. Mais une manière programmatique serait bien. Laissez-moi finir de lire l'article que vous avez recommandé. – Spinor8

+0

'df.drop (..., inplace = True)' modifie '' df' inplace, mais en raison de la façon dont les opérations inplace sont implémentées dans Pandas, il n'y a aucun avantage réel à le faire par rapport à la réaffectation plus directe vers noms de variables. Personnellement, je préfère les fonctions qui renvoient des valeurs sur des fonctions qui modifient des valeurs, car avec la première, la syntaxe d'assignation rend tout à fait clair ce qui est modifié. – unutbu