Dzielenie ramki danych na wiele ramek danych


92

Mam bardzo dużą ramkę danych (około 1 miliona wierszy) z danymi z eksperymentu (60 respondentów).

Chciałbym podzielić ramkę danych na 60 ramek danych (po jednej dla każdego uczestnika).

W ramce danych dataznajduje się zmienna o nazwie 'name', która jest unikalnym kodem dla każdego uczestnika.

Próbowałem wykonać następujące czynności, ale nic się nie dzieje (lub wykonanie nie zatrzymuje się w ciągu godziny). Zamierzam podzielić je datana mniejsze ramki danych i dołączyć je do listy ( 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

Nie pojawia się komunikat o błędzie, skrypt wydaje się działać wiecznie!

Czy jest na to sprytny sposób?

Odpowiedzi:


53

Po pierwsze, twoje podejście jest nieefektywne, ponieważ dołączanie do listy wiersz po podstawie będzie powolne, ponieważ musi okresowo powiększać listę, gdy nie ma wystarczającej ilości miejsca na nowy wpis, rozumienie listy jest lepsze pod tym względem, gdy rozmiar jest określany z przodu i przydzielone raz.

Myślę jednak, że zasadniczo twoje podejście jest trochę marnotrawne, ponieważ masz już ramkę danych, więc po co tworzyć nową dla każdego z tych użytkowników?

Posortowałbym ramkę danych według kolumny 'name', ustawiłbym indeks na ten i jeśli to konieczne, nie upuściłbym kolumny.

Następnie wygeneruj listę wszystkich unikatowych wpisów, a następnie możesz przeprowadzić wyszukiwanie przy użyciu tych wpisów, a co najważniejsze, jeśli sprawdzasz tylko dane, użyj kryteriów wyboru, aby zwrócić widok ramki danych bez ponoszenia kosztownej kopii danych.

Użyj pandas.DataFrame.sort_valuesi 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

Mogę zapytać, dlaczego nie zrobić tego po prostu przez wycięcie ramki danych. Coś jak

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

Hej, presto masz słownik ramek danych, tak jak (myślę) ich chcesz. Potrzebujesz dostępu do jednego? Po prostu wejdź

DataFrameDict['Joe']

Mam nadzieję, że to pomoże


38

Możesz przekonwertować groupbyobiekt na, tuplesa następnie na 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

Nie jest to zalecane , ale możliwe jest tworzenie ramek danych według grup:

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 może Ci pomóc:

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

Następnie możesz pracować z każdą grupą, tak jak z ramką danych dla każdego uczestnika. A metody obiektu DataFrameGroupBy, takie jak (apply, transform, agreate, head, first, last) zwracają obiekt DataFrame.

Lub możesz utworzyć listę groupedi pobrać wszystkie ramki DataFrame według indeksu:

l_grouped = list(grouped)

l_grouped[0][1] - DataFrame dla pierwszej grupy z imieniem.


7

Oprócz odpowiedzi Guseva Slavy możesz użyć grup grupowych:

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

Spowoduje to utworzenie słownika z pogrupowanymi kluczami wskazującymi na odpowiednie partycje. Zaletą jest to, że klucze są utrzymywane i nie znikają w indeksie listy.


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

Oto sposób grupowania (możesz zastosować arbitralne zastosowanie zamiast sumować)

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)

Sum jest cytonizowany, dlatego jest tak szybki

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

Metoda oparta na zrozumieniu list i groupby - która przechowuje wszystkie podzielone ramki danych w zmiennej listy i jest dostępna za pomocą indeksu.

Przykład

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

ans[0]
ans[0].column_name

1
  • Po pierwsze, metoda w OP działa, ale nie jest wydajna. Mogło się wydawać, że trwa wiecznie, ponieważ zbiór danych był długi.
  • Użyj .groupbyw 'method'kolumnie i utwórz dictof DataFramesz unikalnymi 'method'wartościami jako kluczami, z rozszerzeniem dict-comprehension.
    • .groupbyzwraca groupbyobiekt, który zawiera informacje o grupach, gdzie gjest unikatową wartością w 'method'każdej grupie i djest DataFramedla tej grupy.
  • valueKażdego keyIN df_dict, będzie DataFrame, które mogą być dostępne w standardowym sposób df_dict['key'].
  • Oryginalny pytanie chcieliśmy listod DataFrames, które można zrobić zlist-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) lub df_dict['Astrometry'].head(3)
  • W tej grupie są tylko 2
         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) lub 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) lub 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

Alternatywnie

  • Jest to ręczna metoda tworzenia osobnych plikówDataFrames przy użyciu pand: Indeksowanie logiczne
  • Jest to podobne do zaakceptowanej odpowiedzi , ale .locnie jest wymagane.
  • Jest to akceptowalna metoda tworzenia kilku dodatkowych DataFrames.
  • Pythonic sposób, aby utworzyć wiele obiektów, jest umieszczenie ich w pojemniku (na przykład dict, list, generator, itd.), Jak pokazano powyżej.
df1 = df[df.method == 'Astrometry']
df2 = df[df.method == 'Eclipse Timing Variations']

0

Możesz użyć polecenia groupby, jeśli masz już etykiety dla swoich danych.

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

Oto szczegółowy przykład:

Powiedzmy, że chcemy podzielić serię pd za pomocą niektórych etykiet na listę fragmentów Na przykład 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

Odpowiada mu 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

Biegać

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

co zwraca out_lista listz dwóch 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]

Zauważ, że możesz użyć niektórych parametrów od in_seriessiebie, aby pogrupować serię, np.in_series.index.day


-1

Miałem podobny problem. Miałem serię czasową codziennych sprzedaży dla 10 różnych sklepów i 50 różnych artykułów. Musiałem podzielić oryginalną ramkę danych na 500 ramek danych (10 sklepów * 50 sklepów), aby zastosować modele uczenia maszynowego do każdego z nich i nie mogłem tego zrobić ręcznie.

To jest nagłówek ramki danych:

szef Dataframe: df

Stworzyłem dwie listy; jeden dla nazw ramek danych i jeden dla kilku tablic [numer_elementu, numer_sklepu].

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

Gdy te dwie listy będą gotowe, możesz na nich zapętlić, aby utworzyć żądane ramki danych:

         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)

W ten sposób stworzyłem 500 ramek danych.

Mam nadzieję, że to będzie pomocne!

Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.