884 votes

Créer un DataFrame Pandas vide, puis le remplir ?

Je pars de la documentation de pandas DataFrame ici : http://pandas.pydata.org/pandas-docs/stable/dsintro.html

J'aimerais remplir itérativement le DataFrame avec des valeurs dans une sorte de calcul de série chronologique. En gros, j'aimerais initialiser le DataFrame avec les colonnes A, B et les lignes de timestamp, toutes à 0 ou toutes à NaN.

J'ajouterais ensuite des valeurs initiales et je passerais sur ces données en calculant la nouvelle ligne à partir de la ligne précédente, par exemple row[A][t] = row[A][t-1]+1 ou à peu près.

J'utilise actuellement le code ci-dessous, mais je pense que c'est un peu laid et qu'il doit y avoir un moyen de faire cela avec un DataFrame directement, ou juste une meilleure façon en général. Note : J'utilise Python 2.7.

import datetime as dt
import pandas as pd
import scipy as s

if __name__ == '__main__':
    base = dt.datetime.today().date()
    dates = [ base - dt.timedelta(days=x) for x in range(0,10) ]
    dates.sort()

    valdict = {}
    symbols = ['A','B', 'C']
    for symb in symbols:
        valdict[symb] = pd.Series( s.zeros( len(dates)), dates )

    for thedate in dates:
        if thedate > dates[0]:
            for symb in valdict:
                valdict[symb][thedate] = 1+valdict[symb][thedate - dt.timedelta(days=1)]

    print valdict

73 votes

Ne faites jamais croître un DataFrame ! Il est toujours plus économique d'ajouter une liste en python et de la convertir en DataFrame à la fin, tant en termes de mémoire que de performances.

1 votes

@cs95 Qu'est-ce qui est fonctionnellement différent entre .append dans pd et en ajoutant une liste ? Je sais .append dans pandas, on copie l'ensemble des données dans un nouvel objet ', est-ce que python append fonctionne différemment ?

4 votes

@Lamma veuillez trouver les détails dans ma réponse ci-dessous. Lors de l'ajout à df, un nouveau DataFrame est créé à chaque fois en mémoire au lieu d'utiliser celui existant, ce qui est franchement un gaspillage.

1022voto

coldspeed Points 111053

N'agrandissez JAMAIS un DataFrame !

TLDR ; (lisez simplement le texte en gras)

La plupart des réponses ici vous diront comment créer un DataFrame vide et le remplir, mais personne ne vous dira que c'est une mauvaise chose à faire.

Voici mon conseil : Accumuler les données dans une liste, pas dans un DataFrame.

Utilisez une liste pour collecter vos données, puis initialisez un DataFrame lorsque vous êtes prêt. Le format list-of-lists ou list-of-dicts fonctionne, pd.DataFrame accepte les deux.

data = []
for a, b, c in some_function_that_yields_data():
    data.append([a, b, c])

df = pd.DataFrame(data, columns=['A', 'B', 'C'])

Les avantages de cette approche :

  1. Il est toujours plus économique d'ajouter à une liste et de créer un DataFrame en une seule fois. qu'il ne l'est de créer un DataFrame vide (ou un DataFrame contenant des NaN) et d'y ajouter des données encore et encore.

  2. Les listes occupent également moins de mémoire et constituent une structure de données beaucoup plus légère à utiliser. , append, et remove (si nécessaire).

  3. dtypes sont automatiquement déduits (plutôt que d'attribuer object à tous).

  4. A RangeIndex est automatiquement créé pour vos données Au lieu de devoir prendre soin d'affecter l'index correct à la ligne que vous ajoutez à chaque itération.

Si vous n'êtes pas encore convaincus, cela est également mentionné dans le document documentation :

L'ajout itératif de lignes à un DataFrame peut être plus intensif en termes de calcul qu'une simple concaténation. qu'une simple concaténation. Une meilleure solution consiste à ajouter ces lignes à une liste, puis de concaténer cette liste avec le DataFrame DataFrame en une seule fois.

Mais que faire si ma fonction renvoie des DataFrames plus petits que je dois combiner en un grand DataFrame ?

Ce n'est pas grave, vous pouvez toujours faire cela en temps linéaire en faisant croître ou en créant une liste python de petits DataFrames, puis en appelant pd.concat .

small_dfs = []
for small_df in some_function_that_yields_dataframes():
    small_dfs.append(small_df)

large_df = pd.concat(small_dfs, ignore_index=True)

ou, de manière plus concise :

large_df = pd.concat(
    list(some_function_that_yields_dataframes()), ignore_index=True)


Ces options sont horribles

append o concat à l'intérieur d'une boucle

Voici la plus grosse erreur que j'ai vue chez les débutants :

df = pd.DataFrame(columns=['A', 'B', 'C'])
for a, b, c in some_function_that_yields_data():
    df = df.append({'A': i, 'B': b, 'C': c}, ignore_index=True) # yuck
    # or similarly,
    # df = pd.concat([df, pd.Series({'A': i, 'B': b, 'C': c})], ignore_index=True)

La mémoire est réaffectée pour chaque append o concat opération que vous avez. Combinez cela avec une boucle et vous avez un opération de complexité quadratique .

L'autre erreur associée à df.append est que les utilisateurs ont tendance à oublier append n'est pas une fonction in-place Le résultat doit donc être réaffecté. Vous devez également vous préoccuper des dtypes :

df = pd.DataFrame(columns=['A', 'B', 'C'])
df = df.append({'A': 1, 'B': 12.3, 'C': 'xyz'}, ignore_index=True)

df.dtypes
A     object   # yuck!
B    float64
C     object
dtype: object

Traiter des colonnes d'objets n'est jamais une bonne chose, car pandas ne peut pas vectoriser les opérations sur ces colonnes. Vous devrez faire ceci pour résoudre ce problème :

df.infer_objects().dtypes
A      int64
B    float64
C     object
dtype: object

loc à l'intérieur d'une boucle

J'ai aussi vu loc utilisé pour ajouter à un DataFrame qui a été créé vide :

df = pd.DataFrame(columns=['A', 'B', 'C'])
for a, b, c in some_function_that_yields_data():
    df.loc[len(df)] = [a, b, c]

Comme précédemment, vous n'avez pas pré-alloué la quantité de mémoire dont vous avez besoin à chaque fois, donc la mémoire se reconstitue à chaque fois que vous créez un nouveau rang . C'est aussi mauvais que append et encore plus moche.

DataFrame vide de NaNs

Et puis, il y a la création d'un DataFrame de NaNs, et tous les avertissements qui y sont associés.

df = pd.DataFrame(columns=['A', 'B', 'C'], index=range(5))
df
     A    B    C
0  NaN  NaN  NaN
1  NaN  NaN  NaN
2  NaN  NaN  NaN
3  NaN  NaN  NaN
4  NaN  NaN  NaN

Il crée un DataFrame de colonnes d'objets, comme les autres.

df.dtypes
A    object  # you DON'T want this
B    object
C    object
dtype: object

La méthode de l'ajout présente les mêmes problèmes que les méthodes ci-dessus.

for i, (a, b, c) in enumerate(some_function_that_yields_data()):
    df.iloc[i] = [a, b, c]


La preuve est dans le pudding

Le chronométrage de ces méthodes est le moyen le plus rapide de voir à quel point elles diffèrent en termes de mémoire et d'utilité.

enter image description here

Code d'évaluation comparative pour référence.

14 votes

C'est littéralement dans la documentation. "L'ajout itératif de lignes à un DataFrame peut être plus intensif en termes de calcul qu'une concaténation unique. Une meilleure solution consiste à ajouter ces lignes à une liste, puis à concaténer la liste avec le DataFrame original en une seule fois." pandas.pydata.org/pandas-docs/version/0.21/généré/

2 votes

Aussi "Note Il convient de noter que concat() (et donc append()) fait une copie complète des données, et que la réutilisation constante de cette fonction peut créer une baisse significative des performances. Si vous devez utiliser l'opération sur plusieurs ensembles de données, utilisez une compréhension de liste." pandas.pydata.org/pandas-docs/stable/guide_utilisateur/

2 votes

Alors, que dois-je faire lorsque mes données "arrivent" sous forme de listes 1d, une par une, chacune représentant une colonne dans un cadre de données ? Comment puis-je les ajouter ensemble avant de les convertir en un cadre de données ? Il semble que list1.apped(list2) insère une liste dans une autre liste plutôt que d'ajouter une colonne. Merci

425voto

Andy Hayden Points 38010

Voici quelques suggestions :

Utilice date_range pour l'index :

import datetime
import pandas as pd
import numpy as np

todays_date = datetime.datetime.now().date()
index = pd.date_range(todays_date-datetime.timedelta(10), periods=10, freq='D')

columns = ['A','B', 'C']

Remarque : nous pourrions créer un DataFrame vide (avec l'option NaN ) simplement en écrivant :

df_ = pd.DataFrame(index=index, columns=columns)
df_ = df_.fillna(0) # with 0s rather than NaNs

Pour effectuer ce type de calculs pour les données, utilisez un tableau numpy :

data = np.array([np.arange(10)]*3).T

Nous pouvons donc créer le DataFrame :

In [10]: df = pd.DataFrame(data, index=index, columns=columns)

In [11]: df
Out[11]: 
            A  B  C
2012-11-29  0  0  0
2012-11-30  1  1  1
2012-12-01  2  2  2
2012-12-02  3  3  3
2012-12-03  4  4  4
2012-12-04  5  5  5
2012-12-05  6  6  6
2012-12-06  7  7  7
2012-12-07  8  8  8
2012-12-08  9  9  9

2 votes

Pd.date_range() ne fonctionne pas pour moi. J'ai essayé avec DateRange (à partir de l'autocomplétion d'Eclipse), mais cela fonctionne avec des chaînes de caractères comme format de date, non ? L'approche globale fonctionne cependant (j'ai changé l'index en quelque chose d'autre).

0 votes

Est-ce que date_range est une nouvelle fonctionnalité que je pourrais obtenir en mettant à jour ou est-ce qu'elle est dans datatime (j'ai changé pour datetime, car je pensais que c'était ce que vous vouliez dire).

3 votes

Date_range est une fonction d'usine pour la création d'index de temps de date. une nouvelle fonctionnalité dans la version 0.8.0 Je recommande vivement de passer à la dernière version stable (0.9.1), qui contient de nombreuses corrections de bogues et de nouvelles fonctionnalités :)

227voto

geekidharsh Points 2117

Si vous souhaitez simplement créer un cadre de données vide et le remplir ultérieurement avec des cadres de données entrants, essayez ceci :

newDF = pd.DataFrame() #creates a new dataframe that's empty
newDF = newDF.append(oldDF, ignore_index = True) # ignoring index is optional
# try printing some data from newDF
print newDF.head() #again optional 

Dans cet exemple, j'utilise ce document sur les pandas pour créer un nouveau cadre de données, puis en utilisant ajouter pour écrire dans le newDF avec les données de l'oldDF.

Si je dois continuer à ajouter de nouvelles données dans ce newDF à partir de plusieurs un ancien FDF, j'utilise simplement une boucle for pour itérer sur pandas.DataFrame.append()

24 votes

Veuillez noter que append (et de la même façon concat ) copie l'ensemble des données dans un nouvel objet à chaque fois. Par conséquent, l'itération et l'ajout de données peuvent causer et causeront une baisse de performance importante : pandas.pydata.org/pandas-docs/stable/merging.html

4 votes

@MoustafaAAtta Quelles sont les alternatives pour ajouter itérativement des données au dataframe ?

2 votes

@MoustafaAAtta Est la réponse de Fred dans ce post : stackoverflow.com/questions/10715965/ mieux de ce point de vue ?

169voto

Afshin Amiri Points 856

Initialiser le cadre vide avec les noms des colonnes

import pandas as pd

col_names =  ['A', 'B', 'C']
my_df  = pd.DataFrame(columns = col_names)
my_df

Ajouter un nouvel enregistrement à un cadre

my_df.loc[len(my_df)] = [2, 4, 5]

Vous pouvez également passer un dictionnaire :

my_dic = {'A':2, 'B':4, 'C':5}
my_df.loc[len(my_df)] = my_dic 

Ajoutez un autre cadre à votre cadre existant

col_names =  ['A', 'B', 'C']
my_df2  = pd.DataFrame(columns = col_names)
my_df = my_df.append(my_df2)

Considérations sur les performances

Si vous ajoutez des lignes dans une boucle, tenez compte des problèmes de performance. Pour les 1000 premiers enregistrements environ, les performances de "my_df.loc" sont meilleures, mais elles deviennent progressivement plus lentes lorsque le nombre d'enregistrements dans la boucle augmente.

Si vous prévoyez d'effectuer des opérations dans une grande boucle (disons 10M d'enregistrements ou plus), il est préférable d'utiliser un mélange de ces deux méthodes ; remplir un cadre de données avec iloc jusqu'à ce que sa taille atteigne environ 1000, puis l'ajouter au cadre de données original, et vider le cadre de données temporaire. Cela multiplierait vos performances par 10 environ.

0 votes

my_df = my_df.append(my_df2) ne fonctionne pas pour moi, sauf si je spécifie ignore_index=True .

1voto

Ajay Ohri Points 59

Supposons un cadre de données de 19 lignes.

index=range(0,19)
index

columns=['A']
test = pd.DataFrame(index=index, columns=columns)

Conserver la colonne A comme une constante

test['A']=10

Garder la colonne b comme une variable donnée par une boucle

for x in range(0,19):
    test.loc[[x], 'b'] = pd.Series([x], index = [x])

Vous pouvez remplacer le premier x dans pd.Series([x], index = [x]) avec une valeur quelconque

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X