División de marcos de datos en múltiples marcos de datos


92

Tengo un marco de datos muy grande (alrededor de 1 millón de filas) con datos de un experimento (60 encuestados).

Me gustaría dividir el marco de datos en 60 marcos de datos (un marco de datos para cada participante).

En el marco de datos, datahay una variable llamada 'name', que es el código único para cada participante.

He intentado lo siguiente, pero no pasa nada (o la ejecución no se detiene en una hora). Lo que pretendo hacer es dividir dataen marcos de datos más pequeños y agregarlos a una lista ( 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

No recibo un mensaje de error, ¡el script parece ejecutarse para siempre!

¿Existe una forma inteligente de hacerlo?

Respuestas:


53

En primer lugar, su enfoque es ineficiente porque agregar a la lista una fila por base será lento, ya que tiene que hacer crecer la lista periódicamente cuando no hay espacio suficiente para la nueva entrada, las comprensiones de la lista son mejores a este respecto a medida que se determina el tamaño frente y asignado una vez.

Sin embargo, creo que fundamentalmente su enfoque es un poco derrochador ya que ya tiene un marco de datos, así que ¿por qué crear uno nuevo para cada uno de estos usuarios?

Ordenaría el marco de datos por columna 'name', establecería el índice en este y, si es necesario, no soltaría la columna.

Luego genere una lista de todas las entradas únicas y luego puede realizar una búsqueda utilizando estas entradas y, lo que es más importante, si solo consulta los datos, use los criterios de selección para devolver una vista del marco de datos sin incurrir en una costosa copia de datos.

Utilice pandas.DataFrame.sort_valuesy 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

¿Puedo preguntar por qué no hacerlo simplemente cortando el marco de datos? Algo como

#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]

Oye, listo, tienes un diccionario de marcos de datos tal como (creo) los quieres. ¿Necesitas acceder a uno? Solo entra

DataFrameDict['Joe']

Espero que ayude


38

Puede convertir el groupbyobjeto a tuplesy luego a 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

No se recomienda , pero es posible crear DataFrames por grupos:

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 puede ayudarte a:

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

Luego, puede trabajar con cada grupo como con un marco de datos para cada participante. Y los métodos de objeto DataFrameGroupBy como (aplicar, transformar, agregar, encabezar, primero, último) devuelven un objeto DataFrame.

O puede hacer una lista groupedy obtener todos los DataFrame por índice:

l_grouped = list(grouped)

l_grouped[0][1] - DataFrame para el primer grupo con nombre.


7

Además de la respuesta de Gusev Slava, es posible que desee utilizar los grupos de groupby:

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

Esto producirá un diccionario con las claves que ha agrupado, apuntando a las particiones correspondientes. La ventaja es que las claves se mantienen y no desaparecen en el índice de la lista.


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

Aquí hay un grupo por forma (y podría hacer una aplicación arbitraria en lugar de una suma)

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 suma está cythonizada, por eso es tan rápido

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

El método basado en la comprensión de la lista y groupby- Que almacena todo el marco de datos dividido en la variable de lista y se puede acceder a él mediante el índice.

Ejemplo

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

ans[0]
ans[0].column_name

1
  • Primero, el método en el OP funciona, pero no es eficiente. Puede que pareciera que se ejecutaba para siempre, porque el conjunto de datos era largo.
  • Úselo .groupbyen la 'method'columna y cree un dictde DataFramescon 'method'valores únicos como claves, con un dict-comprehension.
    • .groupbydevuelve un groupbyobjeto, que contiene información sobre los grupos, donde ges el valor único en 'method'para cada grupo y des DataFramepara ese grupo.
  • El valuede cada keyen df_dict, habrá una DataFrame, que se puede acceder de la manera estándar, df_dict['key'].
  • La pregunta original quería un listde DataFrames, que se puede hacer con 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) o df_dict['Astrometry'].head(3)
  • Solo hay 2 en este grupo
         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) o 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) o 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

Alternativamente

  • Este es un método manual para crear por separado DataFramesusando pandas: Indexación booleana
  • Es similar a la respuesta aceptada , pero .locno es obligatorio.
  • Este es un método aceptable para crear un par adicional DataFrames.
  • La forma Pythonic para crear varios objetos, es mediante la colocación en un recipiente (por ejemplo dict, list, generator, etc.), como se muestra arriba.
df1 = df[df.method == 'Astrometry']
df2 = df[df.method == 'Eclipse Timing Variations']

0

Puede usar el comando groupby, si ya tiene algunas etiquetas para sus datos.

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

Aquí tienes un ejemplo detallado:

Digamos que queremos particionar una serie pd usando algunas etiquetas en una lista de fragmentos. Por ejemplo, in_serieses:

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

Y su correspondiente label_serieses:

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

correr

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

que devuelve out_listun listde dos 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]

Tenga en cuenta que puede usar algunos parámetros de in_seriessí mismo para agrupar la serie, por ejemplo,in_series.index.day


-1

Tuve un problema similar. Tenía una serie temporal de ventas diarias para 10 tiendas diferentes y 50 artículos diferentes. Necesitaba dividir el marco de datos original en 500 marcos de datos (10 tiendas * 50 tiendas) para aplicar modelos de aprendizaje automático a cada uno de ellos y no pude hacerlo manualmente.

Este es el jefe del marco de datos:

jefe del marco de datos: df

He creado dos listas; uno para los nombres de los marcos de datos y otro para el par de arreglos [item_number, store_number].

    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])

Y una vez que las dos listas estén listas, puede recorrerlas para crear los marcos de datos que desee:

         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 esta manera he creado 500 marcos de datos.

¡Esperamos que esto sea útil!

Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.