Dividindo dataframe em múltiplos dataframes

96

Tenho um dataframe muito grande (cerca de 1 milhão de linhas) com dados de um experimento (60 entrevistados).

Eu gostaria de dividir o dataframe em 60 dataframes (um dataframe para cada participante).

No dataframe,, datahá uma variável chamada 'name', que é o código exclusivo de cada participante.

Tentei o seguinte, mas nada acontece (ou a execução não para em uma hora). O que pretendo fazer é dividir dataem dataframes menores e anexá-los a uma 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

Não recebo uma mensagem de erro, o script simplesmente parece funcionar para sempre!

Existe uma maneira inteligente de fazer isso?

Martin Petri Bagger
fonte

Respostas:

55

Em primeiro lugar, sua abordagem é ineficiente porque o acréscimo à lista em uma base será lento, pois tem que aumentar periodicamente a lista quando não há espaço suficiente para a nova entrada, as compreensões de lista são melhores a este respeito, pois o tamanho é determinado frente e alocado uma vez.

No entanto, acho que fundamentalmente sua abordagem é um pouco inútil, pois você já tem um dataframe, então por que criar um novo para cada um desses usuários?

Eu classificaria o dataframe por coluna 'name', definiria o índice como este e, se necessário, não descartaria a coluna.

Em seguida, gere uma lista de todas as entradas exclusivas e, em seguida, você pode executar uma pesquisa usando essas entradas e, principalmente, se você apenas consultar os dados, use os critérios de seleção para retornar uma visualização no dataframe sem incorrer em uma cópia de dados dispendiosa.

Use pandas.DataFrame.sort_valuese 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'
EdChum
fonte
75

Posso perguntar por que não fazer isso simplesmente fatiando o quadro de dados. 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]

Ei, pronto, você tem um dicionário de frames de dados exatamente como (eu acho) você os quer. Precisa acessar um? Basta entrar

DataFrameDict['Joe']

espero que ajude

Woody Pride
fonte
40

Você pode converter o groupbyobjeto em tuplese depois em 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

Não é recomendado , mas é possível criar 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
jezrael
fonte
19

Fácil:

[v for k, v in df.groupby('name')]
Daniel Braun
fonte
17

O Groupby pode ajudá-lo a:

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

Depois, você pode trabalhar com cada grupo como um dataframe para cada participante. E os métodos de objeto DataFrameGroupBy, como (aplicar, transformar, agregar, iniciar, primeiro, último), retornam um objeto DataFrame.

Ou você pode fazer uma lista groupede obter todos os DataFrames por índice:

l_grouped = list(grouped)

l_grouped[0][1] - DataFrame para primeiro grupo com nome.

Gusev Slava
fonte
7

Além da resposta de Gusev Slava, você pode querer usar grupos de groupby:

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

Isso produzirá um dicionário com as chaves que você agrupou, apontando para as partições correspondentes. A vantagem é que as chaves são mantidas e não desaparecem no índice da lista.

Quickbeam2k1
fonte
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

Aqui está uma maneira de agrupar (e você pode fazer uma aplicação arbitrária em vez de soma)

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)

A soma é citonizada, por isso é tão 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
Jeff
fonte
1

O método baseado na compreensão da lista e groupby - que armazena todo o dataframe dividido em variável de lista e pode ser acessado usando o índice.

Exemplo

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

ans[0]
ans[0].column_name
Ram Prajapati
fonte
1
  • Primeiro, o método no OP funciona, mas não é eficiente. Pode ter parecido funcionar para sempre, porque o conjunto de dados era longo.
  • Use .groupbyna 'method'coluna e crie um dictde DataFramescom 'method'valores exclusivos como as chaves, com um dict-comprehension.
    • .groupbyretorna um groupbyobjeto, que contém informações sobre os grupos, onde gé o valor exclusivo em 'method'para cada grupo e dé o DataFramepara esse grupo.
  • O valuede cada keyem df_dict, será um DataFrame, que pode ser acessado da forma padrão df_dict['key'],.
  • A pergunta original queria um listde DataFrames, o que pode ser feito com umlist-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)
  • Existem apenas 2 neste 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) 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

alternativamente

  • Este é um método manual para criar separado DataFramesusando pandas: Indexação Booleana
  • Isso é semelhante à resposta aceita , mas .locnão é obrigatório.
  • Este é um método aceitável para a criação de alguns extras DataFrames .
  • A maneira Python para criar vários objectos, é colocando-os num recipiente (por exemplo dict, list, generator, etc.), como mostrado acima.
df1 = df[df.method == 'Astrometry']
df2 = df[df.method == 'Eclipse Timing Variations']
Trenton McKinney
fonte
0

Você pode usar o comando groupby, se já tiver alguns rótulos para seus dados.

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

Aqui está um exemplo detalhado:

Digamos que queremos particionar uma série pd usando alguns rótulos em uma lista de blocos. Por exemplo, in_seriesé:

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

E seu correspondente label_seriesé:

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

Corre

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

que retorna out_listum listde dois 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]

Observe que você pode usar alguns parâmetros dele in_seriesmesmo para agrupar a série, por exemplo,in_series.index.day

idnavid
fonte
-1

Eu tive um problema semelhante. Tive uma série temporal de vendas diárias para 10 lojas diferentes e 50 itens diferentes. Eu precisei dividir o dataframe original em 500 dataframes (10stores * 50stores) para aplicar os modelos de Machine Learning a cada um deles e não pude fazer isso manualmente.

Este é o chefe do dataframe:

chefe do dataframe: df

Eu criei duas listas; um para os nomes dos dataframes e um para o par de array [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])

E quando as duas listas estiverem prontas, você pode repeti-las para criar os dataframes que deseja:

         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)

Desta forma, criei 500 dataframes.

Espero que isso seja útil!

Luigi Bungaro
fonte