Pandy: pogrupuj według tygodnia kalendarzowego, a następnie pogrupuj słupki dla rzeczywistego datetime

EDIT

Znalazłem całkiem fajne rozwiązanie i zamieściłem je poniżej jako odpowiedź. Wynik będzie wyglądał tak:

Tutaj wpisz opis obrazka


Niektóre przykładowe dane, które możesz wygenerować dla tego problemu:

codes = list('ABCDEFGH'); 
dates = pd.Series(pd.date_range('2013-11-01', '2014-01-31')); 
dates = dates.append(dates)
dates.sort()
df = pd.DataFrame({'amount': np.random.randint(1, 10, dates.size), 'col1': np.random.choice(codes, dates.size), 'col2': np.random.choice(codes, dates.size), 'date': dates})

W wyniku:

In [55]: df
Out[55]:
    amount col1 col2       date
0        1    D    E 2013-11-01
0        5    E    B 2013-11-01
1        5    G    A 2013-11-02
1        7    D    H 2013-11-02
2        5    E    G 2013-11-03
2        4    H    G 2013-11-03
3        7    A    F 2013-11-04
3        3    A    A 2013-11-04
4        1    E    G 2013-11-05
4        7    D    C 2013-11-05
5        5    C    A 2013-11-06
5        7    H    F 2013-11-06
6        1    G    B 2013-11-07
6        8    D    A 2013-11-07
7        1    B    H 2013-11-08
7        8    F    H 2013-11-08
8        3    A    E 2013-11-09
8        1    H    D 2013-11-09
9        3    B    D 2013-11-10
9        1    H    G 2013-11-10
10       6    E    E 2013-11-11
10       6    F    E 2013-11-11
11       2    G    B 2013-11-12
11       5    H    H 2013-11-12
12       5    F    G 2013-11-13
12       5    G    B 2013-11-13
13       8    H    B 2013-11-14
13       6    G    F 2013-11-14
14       9    F    C 2013-11-15
14       4    H    A 2013-11-15
..     ...  ...  ...        ...
77       9    A    B 2014-01-17
77       7    E    B 2014-01-17
78       4    F    E 2014-01-18
78       6    B    E 2014-01-18
79       6    A    H 2014-01-19
79       3    G    D 2014-01-19
80       7    E    E 2014-01-20
80       6    G    C 2014-01-20
81       9    H    G 2014-01-21
81       9    C    B 2014-01-21
82       2    D    D 2014-01-22
82       7    D    A 2014-01-22
83       6    G    B 2014-01-23
83       1    A    G 2014-01-23
84       9    B    D 2014-01-24
84       7    G    D 2014-01-24
85       7    A    F 2014-01-25
85       9    B    H 2014-01-25
86       9    C    D 2014-01-26
86       5    E    B 2014-01-26
87       3    C    H 2014-01-27
87       7    F    D 2014-01-27
88       3    D    G 2014-01-28
88       4    A    D 2014-01-28
89       2    F    A 2014-01-29
89       8    D    A 2014-01-29
90       1    A    G 2014-01-30
90       6    C    A 2014-01-30
91       6    H    C 2014-01-31
91       2    G    F 2014-01-31

[184 rows x 4 columns]

Chciałbym pogrupować według tygodnia kalendarzowego i według wartości col1. Tak:

kw = lambda x: x.isocalendar()[1]
grouped = df.groupby([df['date'].map(kw), 'col1'], sort=False).agg({'amount': 'sum'})

W wyniku:

In [58]: grouped
Out[58]:
           amount
date col1
44   D          8
     E         10
     G          5
     H          4
45   D         15
     E          1
     G          1
     H          9
     A         13
     C          5
     B          4
     F          8
46   E          7
     G         13
     H         17
     B          9
     F         23
47   G         14
     H          4
     A         40
     C          7
     B         16
     F         13
48   D          7
     E         16
     G          9
     H          2
     A          7
     C          7
     B          2
...           ...
1    H         14
     A         14
     B         15
     F         19
2    D         13
     H         13
     A         13
     B         10
     F         32
3    D          8
     E         18
     G          3
     H          6
     A         30
     C          9
     B          6
     F          5
4    D          9
     E         12
     G         19
     H          9
     A          8
     C         18
     B         18
5    D         11
     G          2
     H          6
     A          5
     C          9
     F          9

[87 rows x 1 columns]

W takim razie chcę aby Wykres był generowany w ten sposób: Tutaj wpisz opis obrazka Czyli: Kalendarz-Tydzień i rok (datetime) na osi x i dla każdego zgrupowanego col1 jednego paska.

Problem, z którym się borykam, polega na tym, że mam tylko liczby całkowite opisujące tydzień kalendarzowy (KW na wykresie), ale jakoś muszę cofnąć na nim datę, aby kleszcze oznaczały również rok. Ponadto nie mogę wykreślić tylko pogrupowanego tygodnia kalendarzowego, ponieważ potrzebuję poprawnej kolejności pozycji (kw 47, kw 48 (Rok 2013) muszą być po lewej stronie kw 1 (bo to jest 2014)).


EDIT

I figured out from proszę.: http://pandas.pydata.org/pandas-docs/stable/visualization.html#visualization-barplot że zgrupowane słupki muszą być kolumnami zamiast wierszami. Więc pomyślałem o tym, jak przekształcić Dane i znalazłem metodę pivot, która okazuje się być świetną funkcją. reset_index jest potrzebne do przekształcenia multiindex w kolumny. Na końcu wypełniam NaN S przez zero:

A = grouped.reset_index().pivot(index='date', columns='col1', values='amount').fillna(0)

Przekształca dane na:

col1   A   B   C   D   E   F   G   H
date
1      4  31   0   0   0  18  13   8
2      0  12  13  22   1  17   0   8
3      3  10   4  13  12   8   7   6
4     17   0  10   7   0  25   7   4
5      7   0   7   9   8   6   0   7
44     0   0   2  11   7   0   0   2
45     9   3   2  14   0  16  21   2
46     0  14   7   2  17  13  11   8
47     5  13   0  15  19   7   5  10
48    15   8  12   2  20   4   7   6
49    20   0   0  18  22  17  11   0
50     7  11   8   6   5   6  13  10
51     8  26   0   0   5   5  16   9
52     8  13   7   5   4  10   0  11

Który wygląda jak przykładowe dane w dokumentach do wykreślenia zgrupowane paski:

A. plot(kind='bar')

Dostaje to:

Tutaj wpisz opis obrazka

Natomiast mam problem z osią, ponieważ jest ona teraz posortowana (od 1-52), co jest rzeczywiście błędne, ponieważ tydzień kalendarzowy 52 należy do roku 2013 w tym przypadku... Jakieś pomysły, jak połączyć z powrotem rzeczywistą datetime dla tygodni kalendarzowych i użyć ich jako wskazań osi x?

Author: tim, 2014-06-13

3 answers

Myślę, że resample('W') jest lepszym sposobem na to - domyślnie grupuje się według tygodni kończących się w niedzielę ('W' to to samo co 'w-SUN'), ale możesz określić, co chcesz.

W twoim przykładzie spróbuj tego:

grouped = (df
    .groupby('col1')                
    .apply(lambda g:               # work on groups of col1
        g.set_index('date')        
        [['amount']]
        .resample('W', how='sum')  # sum the amount field across weeks
    )
    .unstack(level=0)              # pivot the col1 index rows to columns
    .fillna(0)
)
grouped.columns=grouped.columns.droplevel()   # drop the 'col1' part of the multi-index column names
print grouped
grouped.plot(kind='bar')

Który powinien wydrukować Twoją tabelę danych i stworzyć wykres podobny do Twojej, ale z" prawdziwymi " etykietami daty:

col1         A   B   C   D   E   F   G   H
date                                      
2013-11-03  18  0   9   0   8   0   0   4 
2013-11-10  4   11  0   1   16  2   15  2 
2013-11-17  10  14  19  8   13  6   9   8 
2013-11-24  10  13  13  0   0   13  15  10
2013-12-01  6   3   19  8   8   17  8   12
2013-12-08  5   15  5   7   12  0   11  8 
2013-12-15  8   6   11  11  0   16  6   14
2013-12-22  16  3   13  8   8   11  15  0 
2013-12-29  1   3   6   10  7   7   17  15
2014-01-05  12  7   10  11  6   0   1   12
2014-01-12  13  0   17  0   23  0   10  12
2014-01-19  10  9   2   3   8   1   18  3 
2014-01-26  24  9   8   1   19  10  0   3 
2014-02-02  1   6   16  0   0   10  8   13
 10
Author: patricksurry,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-01-06 15:39:18

Dodaj tydzień do 52 razy w roku, tak aby tygodnie były uporządkowane "według roku". Ustaw Etykietę tick z powrotem, , która może być nietrywialna , na to, co chcesz.


To, czego chcesz, to aby tygodnie tak wzrosły

nth week → (n+1)th week → (n+2)th week → etc.

Ale kiedy masz Nowy Rok to zamiast spada o 51 (52 → 1).

Aby to zrównoważyć, należy zauważyć, że rok zwiększa się o jeden. Więc dodaj rok wzrost pomnożony przez 52 i całkowita zmiana będzie -51 + 52 = 1 jak chciał.
 2
Author: Veedrac,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-05-23 11:55:02

[7]} dobra, sam odpowiadam na pytanie, Kiedy w końcu to rozgryzłem. Kluczem nie jest grupowanie według tygodnia kalendarzowego (ponieważ stracisz informacje o roku), ale raczej Grupowanie według ciągu zawierającego tydzień kalendarzowy i rok.

Następnie zmień układ (przekształcanie), jak wspomniano w pytaniu, używając pivot. Data będzie indeksem. Użyj reset_index(), aby uczynić bieżący date-index kolumną, a zamiast tego uzyskaj zakres liczb całkowitych jako indeks (który jest następnie w odpowiedniej kolejności do wykreślenia (najniższy rok / tydzień kalendarzowy to indeks 0, a najwyższy rok / tydzień kalendarzowy to najwyższa liczba całkowita).

Wybierz kolumnę date - jako nową zmienną ticks jako listę i usuń tę kolumnę z ramki danych. Teraz wykreśl paski i po prostu Ustaw etykiety xticków na ticks. Kompletne rozwiązanie jest dość proste i tutaj:

codes = list('ABCDEFGH'); 
dates = pd.Series(pd.date_range('2013-11-01', '2014-01-31')); 
dates = dates.append(dates)
dates.sort()
df = pd.DataFrame({'amount': np.random.randint(1, 10, dates.size), 'col1': np.random.choice(codes, dates.size), 'col2': np.random.choice(codes, dates.size), 'date': dates})

kw = lambda x: x.isocalendar()[1]; 
kw_year = lambda x: str(x.year) + ' - ' + str(x.isocalendar()[1])
grouped = df.groupby([df['date'].map(kw_year), 'col1'], sort=False, as_index=False).agg({'amount': 'sum'})
A = grouped.pivot(index='date', columns='col1', values='amount').fillna(0).reset_index()

ticks = A.date.values.tolist()
del A['date']
ax = A.plot(kind='bar')
ax.set_xticklabels(ticks)

Wynik:

Tutaj wpisz opis obrazka

 1
Author: tim,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-06-13 12:36:22