Diviser le dataframe en plusieurs dataframes


92

J'ai un très grand dataframe (environ 1 million de lignes) avec des données d'une expérience (60 répondants).

Je souhaite diviser le dataframe en 60 dataframes (un dataframe pour chaque participant).

Dans la trame de données, datail existe une variable appelée 'name', qui est le code unique de chaque participant.

J'ai essayé ce qui suit, mais rien ne se passe (ou l'exécution ne s'arrête pas dans l'heure). Ce que j'ai l'intention de faire est de diviser le dataen plus petits dataframes, et de les ajouter à une liste ( datalist):

import pandas as pd

def splitframe(data, name='name'):
    
    n = data[name][0]

    df = pd.DataFrame(columns=data.columns)

    datalist = []

    for i in range(len(data)):
        if data[name][i] == n:
            df = df.append(data.iloc[i])
        else:
            datalist.append(df)
            df = pd.DataFrame(columns=data.columns)
            n = data[name][i]
            df = df.append(data.iloc[i])
        
    return datalist

Je n'obtiens pas de message d'erreur, le script semble fonctionner pour toujours!

Y a-t-il une manière intelligente de le faire?

Réponses:


53

Tout d'abord, votre approche est inefficace car l'ajout à la liste ligne par base sera lent car il doit périodiquement agrandir la liste lorsqu'il n'y a pas suffisamment d'espace pour la nouvelle entrée, les compréhensions de liste sont meilleures à cet égard car la taille est déterminée avant et attribué une fois.

Cependant, je pense que fondamentalement, votre approche est un peu inutile car vous avez déjà un dataframe alors pourquoi en créer un nouveau pour chacun de ces utilisateurs?

Je trierais le dataframe par colonne 'name', définirais l'index pour être ceci et si nécessaire ne pas supprimer la colonne.

Générez ensuite une liste de toutes les entrées uniques, puis vous pouvez effectuer une recherche à l'aide de ces entrées et, surtout, si vous n'interrogez que les données, utilisez les critères de sélection pour renvoyer une vue sur la trame de données sans encourir une copie de données coûteuse.

Utilisez pandas.DataFrame.sort_valueset pandas.DataFrame.set_index:

# sort the dataframe
df.sort_values(by='name', axis=1, inplace=True)

# set the index to be this and don't drop
df.set_index(keys=['name'], drop=False,inplace=True)

# get a list of names
names=df['name'].unique().tolist()

# now we can perform a lookup on a 'view' of the dataframe
joe = df.loc[df.name=='joe']

# now you can query all 'joes'

74

Puis-je demander pourquoi ne pas simplement le faire en découpant la trame de données. Quelque chose comme

#create some data with Names column
data = pd.DataFrame({'Names': ['Joe', 'John', 'Jasper', 'Jez'] *4, 'Ob1' : np.random.rand(16), 'Ob2' : np.random.rand(16)})

#create unique list of names
UniqueNames = data.Names.unique()

#create a data frame dictionary to store your data frames
DataFrameDict = {elem : pd.DataFrame for elem in UniqueNames}

for key in DataFrameDict.keys():
    DataFrameDict[key] = data[:][data.Names == key]

Hé hop, vous avez un dictionnaire de trames de données comme (je pense) vous les voulez. Besoin d'y accéder? Entrez simplement

DataFrameDict['Joe']

J'espère que ça t'as aidé


38

Vous pouvez convertir un groupbyobjet en tuples, puis en dict:

df = pd.DataFrame({'Name':list('aabbef'),
                   'A':[4,5,4,5,5,4],
                   'B':[7,8,9,4,2,3],
                   'C':[1,3,5,7,1,0]}, columns = ['Name','A','B','C'])

print (df)
  Name  A  B  C
0    a  4  7  1
1    a  5  8  3
2    b  4  9  5
3    b  5  4  7
4    e  5  2  1
5    f  4  3  0

d = dict(tuple(df.groupby('Name')))
print (d)
{'b':   Name  A  B  C
2    b  4  9  5
3    b  5  4  7, 'e':   Name  A  B  C
4    e  5  2  1, 'a':   Name  A  B  C
0    a  4  7  1
1    a  5  8  3, 'f':   Name  A  B  C
5    f  4  3  0}

print (d['a'])
  Name  A  B  C
0    a  4  7  1
1    a  5  8  3

Il n'est pas recommandé , mais possible de créer des DataFrames par groupes:

for i, g in df.groupby('Name'):
    globals()['df_' + str(i)] =  g

print (df_a)
  Name  A  B  C
0    a  4  7  1
1    a  5  8  3


16

Groupby peut vous aider:

grouped = data.groupby(['name'])

Ensuite, vous pouvez travailler avec chaque groupe comme avec un dataframe pour chaque participant. Et les méthodes d'objet DataFrameGroupBy telles que (appliquer, transformer, agréger, head, first, last) renvoient un objet DataFrame.

Ou vous pouvez créer une liste à partir de groupedet obtenir tous les DataFrame par index:

l_grouped = list(grouped)

l_grouped[0][1] - DataFrame pour le premier groupe avec le prénom.


7

En plus de la réponse de Gusev Slava, vous pouvez utiliser les groupes de groupby:

{key: df.loc[value] for key, value in df.groupby("name").groups.items()}

Cela donnera un dictionnaire avec les clés que vous avez regroupées, pointant vers les partitions correspondantes. L'avantage est que les clés sont conservées et ne disparaissent pas dans l'index de la liste.


3
In [28]: df = DataFrame(np.random.randn(1000000,10))

In [29]: df
Out[29]: 
<class 'pandas.core.frame.DataFrame'>
Int64Index: 1000000 entries, 0 to 999999
Data columns (total 10 columns):
0    1000000  non-null values
1    1000000  non-null values
2    1000000  non-null values
3    1000000  non-null values
4    1000000  non-null values
5    1000000  non-null values
6    1000000  non-null values
7    1000000  non-null values
8    1000000  non-null values
9    1000000  non-null values
dtypes: float64(10)

In [30]: frames = [ df.iloc[i*60:min((i+1)*60,len(df))] for i in xrange(int(len(df)/60.) + 1) ]

In [31]: %timeit [ df.iloc[i*60:min((i+1)*60,len(df))] for i in xrange(int(len(df)/60.) + 1) ]
1 loops, best of 3: 849 ms per loop

In [32]: len(frames)
Out[32]: 16667

Voici une méthode groupby (et vous pourriez faire une application arbitraire plutôt qu'une somme)

In [9]: g = df.groupby(lambda x: x/60)

In [8]: g.sum()    

Out[8]: 
<class 'pandas.core.frame.DataFrame'>
Int64Index: 16667 entries, 0 to 16666
Data columns (total 10 columns):
0    16667  non-null values
1    16667  non-null values
2    16667  non-null values
3    16667  non-null values
4    16667  non-null values
5    16667  non-null values
6    16667  non-null values
7    16667  non-null values
8    16667  non-null values
9    16667  non-null values
dtypes: float64(10)

La somme est cythonisée, c'est pourquoi c'est si rapide

In [10]: %timeit g.sum()
10 loops, best of 3: 27.5 ms per loop

In [11]: %timeit df.groupby(lambda x: x/60)
1 loops, best of 3: 231 ms per loop

1

La méthode basée sur la compréhension de liste et groupby- qui stocke toutes les données fractionnées dans une variable de liste et est accessible à l'aide de l'index.

Exemple

ans = [pd.DataFrame(y) for x, y in DF.groupby('column_name', as_index=False)]

ans[0]
ans[0].column_name

1
  • Premièrement, la méthode dans l'OP fonctionne, mais n'est pas efficace. Cela a peut-être semblé durer éternellement, car l'ensemble de données était long.
  • Utilisez .groupbysur la 'method'colonne, et de créer un dictdes DataFramesavec uniques 'method'valeurs telles que les touches, avec un dict-comprehension.
    • .groupbyrenvoie un groupbyobjet, qui contient des informations sur les groupes, où gest la valeur unique 'method'de chaque groupe et dest le DataFramepour ce groupe.
  • Le valuede chacun keydans df_dict, sera un DataFrame, qui peut être accédé de manière standard,df_dict['key'] .
  • La question originale voulait un listde DataFrames, ce qui peut être fait avec unlist-comprehension
    • df_list = [d for _, d in df.groupby('method')]
import pandas as pd
import seaborn as sns  # for test dataset

# load data for example
df = sns.load_dataset('planets')

# display(df.head())
            method  number  orbital_period   mass  distance  year
0  Radial Velocity       1         269.300   7.10     77.40  2006
1  Radial Velocity       1         874.774   2.21     56.95  2008
2  Radial Velocity       1         763.000   2.60     19.84  2011
3  Radial Velocity       1         326.030  19.40    110.62  2007
4  Radial Velocity       1         516.220  10.50    119.47  2009


# Using a dict-comprehension, the unique 'method' value will be the key
df_dict = {g: d for g, d in df.groupby('method')}

print(df_dict.keys())
[out]:
dict_keys(['Astrometry', 'Eclipse Timing Variations', 'Imaging', 'Microlensing', 'Orbital Brightness Modulation', 'Pulsar Timing', 'Pulsation Timing Variations', 'Radial Velocity', 'Transit', 'Transit Timing Variations'])

# or a specific name for the key, using enumerate (e.g. df1, df2, etc.)
df_dict = {f'df{i}': d for i, (g, d) in enumerate(df.groupby('method'))}

print(df_dict.keys())
[out]:
dict_keys(['df0', 'df1', 'df2', 'df3', 'df4', 'df5', 'df6', 'df7', 'df8', 'df9'])
  • df_dict['df1].head(3) ou df_dict['Astrometry'].head(3)
  • Il n'y en a que 2 dans ce groupe
         method  number  orbital_period  mass  distance  year
113  Astrometry       1          246.36   NaN     20.77  2013
537  Astrometry       1         1016.00   NaN     14.98  2010
  • df_dict['df2].head(3) ou df_dict['Eclipse Timing Variations'].head(3)
                       method  number  orbital_period  mass  distance  year
32  Eclipse Timing Variations       1         10220.0  6.05       NaN  2009
37  Eclipse Timing Variations       2          5767.0   NaN    130.72  2008
38  Eclipse Timing Variations       2          3321.0   NaN    130.72  2008
  • df_dict['df3].head(3) ou df_dict['Imaging'].head(3)
     method  number  orbital_period  mass  distance  year
29  Imaging       1             NaN   NaN     45.52  2005
30  Imaging       1             NaN   NaN    165.00  2007
31  Imaging       1             NaN   NaN    140.00  2004

Alternativement

  • Il s'agit d'une méthode manuelle pour créer séparément à l' DataFramesaide de pandas: Indexation booléenne
  • Ceci est similaire à la réponse acceptée , mais .locn'est pas obligatoire.
  • C'est une méthode acceptable pour créer un couple supplémentaire DataFrames.
  • La façon dont pythonique pour créer plusieurs objets, est de les placer dans un récipient (par exemple dict, list, generator, etc.), comme indiqué ci - dessus.
df1 = df[df.method == 'Astrometry']
df2 = df[df.method == 'Eclipse Timing Variations']

0

Vous pouvez utiliser la commande groupby, si vous avez déjà des étiquettes pour vos données.

 out_list = [group[1] for group in in_series.groupby(label_series.values)]

Voici un exemple détaillé:

Disons que nous voulons partitionner une série pd en utilisant des étiquettes dans une liste de morceaux Par exemple, in_seriesest:

2019-07-01 08:00:00   -0.10
2019-07-01 08:02:00    1.16
2019-07-01 08:04:00    0.69
2019-07-01 08:06:00   -0.81
2019-07-01 08:08:00   -0.64
Length: 5, dtype: float64

Et son correspondant label_seriesest:

2019-07-01 08:00:00   1
2019-07-01 08:02:00   1
2019-07-01 08:04:00   2
2019-07-01 08:06:00   2
2019-07-01 08:08:00   2
Length: 5, dtype: float64

Courir

out_list = [group[1] for group in in_series.groupby(label_series.values)]

qui renvoie out_listun listde deux pd.Series:

[2019-07-01 08:00:00   -0.10
2019-07-01 08:02:00   1.16
Length: 2, dtype: float64,
2019-07-01 08:04:00    0.69
2019-07-01 08:06:00   -0.81
2019-07-01 08:08:00   -0.64
Length: 3, dtype: float64]

Notez que vous pouvez utiliser certains paramètres de in_serieslui-même pour regrouper la série, par exemple,in_series.index.day


-1

J'ai eu un problème similaire. J'ai eu une série chronologique de ventes quotidiennes pour 10 magasins différents et 50 articles différents. J'avais besoin de diviser le dataframe original en 500 dataframes (10stores * 50stores) pour appliquer des modèles Machine Learning à chacun d'eux et je ne pouvais pas le faire manuellement.

C'est la tête du dataframe:

responsable du dataframe: df

J'ai créé deux listes; un pour les noms des dataframes et un pour le couple de tableaux [numéro_élément, numéro_magasin].

    list=[]
    for i in range(1,len(items)*len(stores)+1):
    global list
    list.append('df'+str(i))

    list_couple_s_i =[]
    for item in items:
          for store in stores:
                  global list_couple_s_i
                  list_couple_s_i.append([item,store])

Et une fois que les deux listes sont prêtes, vous pouvez les boucler pour créer les dataframes que vous souhaitez:

         for name, it_st in zip(list,list_couple_s_i):
                   globals()[name] = df.where((df['item']==it_st[0]) & 
                                                (df['store']==(it_st[1])))
                   globals()[name].dropna(inplace=True)

De cette façon, j'ai créé 500 dataframes.

J'espère que cela vous sera utile!

En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.