1
votes

Pandas: regrouper par date spécifique

Je souhaite diviser le dataframe par des dates spécifiques et le niveau limite au 31 mars. Cela signifie que tout ce qui est arrivé avant le 31 mars devrait appartenir à une trame de données et tout ce qui suit devrait appartenir à la trame de données suivante et ainsi de suite. Les données ressemblent à ceci:

import pandas as pd


path = 'C:/Users/Adrian/Desktop/'
df = pd.read_csv(path + "trailing.csv", low_memory=False, usecols=[0, 3, 6])
df.rename(columns={'report_dte': 'date'}, inplace=True)
df['date'] = pd.to_datetime(df['date'], format="%d/%m/%Y")
df = df.sort_values(["date"], ascending=True)
df['t_rev'] = 0

df2 = df.groupby("c_name")
counter = 0
for c_name, df_name in df2:
    counter += 1

    print(df_name.sort_values(['date'], ascending=True))
    print(len(df_name.index))
    if counter == 1:
        break

Résultat attendu:

df1:
                        c_name       date  revenue  t_rev
    310260  1-800 CONTACTS INC 1997-01-31    3.628      0

df2:
                        c_name       date  revenue  t_rev
    296014  1-800 CONTACTS INC 1998-01-31    6.970      0

df3:
                        c_name       date  revenue  t_rev
    291248  1-800 CONTACTS INC 1998-05-06   10.429      0
    287356  1-800 CONTACTS INC 1998-08-11   12.801      0
    283720  1-800 CONTACTS INC 1998-11-10   18.419      0
    279837  1-800 CONTACTS INC 1999-02-24   18.227      0

df4: 

                         c_name       date  revenue  t_rev
    277333  1-800 CONTACTS INC 1999-04-27   22.304      0
    274650  1-800 CONTACTS INC 1999-07-20   23.960      0
    269885  1-800 CONTACTS INC 1999-10-26   26.890      0
    265851  1-800 CONTACTS INC 2000-02-07   25.371      0

Mon code pour l'instant est le suivant:

                    c_name       date  revenue  t_rev
310260  1-800 CONTACTS INC 1997-01-31    3.628      0
296014  1-800 CONTACTS INC 1998-01-31    6.970      0
291248  1-800 CONTACTS INC 1998-05-06   10.429      0
287356  1-800 CONTACTS INC 1998-08-11   12.801      0
283720  1-800 CONTACTS INC 1998-11-10   18.419      0
279837  1-800 CONTACTS INC 1999-02-24   18.227      0
277333  1-800 CONTACTS INC 1999-04-27   22.304      0
274650  1-800 CONTACTS INC 1999-07-20   23.960      0
269885  1-800 CONTACTS INC 1999-10-26   26.890      0
265851  1-800 CONTACTS INC 2000-02-07   25.371      0

Les solutions @Josh Friedlander, @Jeril et @KeepAlive fonctionnent comme prévu. Merci à tous!


3 commentaires

Mieux vaut utiliser loc et attribuer de nouveaux dfs que d'utiliser groupby


@JoshFriedlander Désolé de vous déranger, pouvez-vous expliquer un peu plus, s'il vous plaît? Dois-je utiliser loc pour diviser le dataframe en dataframes pour chaque entreprise? Ou que voulez-vous dire exactement?


Voir la réponse de Jeril, mais la deuxième partie - c'est ce que je suggère


3 Réponses :


2
votes

Je n'ai pas essayé d'exécuter ce code, mais je suppose que cela devrait fonctionner.

df_dict = {}
for year in df['year'].unique():
    df_dict[year] = df[df['year' == year]]

Assurez-vous que la colonne 'date' est de type pd.datetime . Ce que nous faisons ici, c'est que nous essayons de créer une colonne basée sur l'exercice, et de regrouper en fonction de cela.

Si vous souhaitez créer une base de données pour chaque année, vous pouvez parcourir le colonne "année" et créez une nouvelle trame de données au fur et à mesure.

def getFiscalYear(dt):
    year = dt.year
    if dt.month<4: year -= 1
    return year

df['year'] = df['date'].apply(getFiscalYear)
df.groupby('year')


1 commentaires

Au lieu de df [df ['year' == year]] utilisez df.loc [df ['year'] == year]



1
votes

Que diriez-vous de faire

dfs = []
#...
    #...
    dfs.append(
        df.loc[(min_date < df.date64) & (df.date64 <= max_date)].copy()
    )

qui a injecté df1 , df2 , df3 et df4 dans l'environnement global. Mettre de côté la discussion pour savoir si c'est une bonne pratique ou non.


Il vaut mieux éviter de jouer directement à l'intérieur de l'environnement global comme ceci: enregistrer les versions tronquées de df code > à l'intérieur, par exemple, d'un dictionnaire appelé dfs , est recommandé:

dfs = {}
#...
    #...
    dfs[y+1] = df.loc[
        (min_date < df.date64) & (df.date64 <= max_date)
    ].copy()

ou faire la même chose avec un liste

import datetime as dt

years        = range(1997, 2000 + 1)
df['date64'] = df.date.astype("datetime64")

for y,year in enumerate(years):
    min_date = dt.datetime(year    , 3, 31)
    max_date = dt.datetime(year + 1, 3, 31)

    globals()[f'df{y+1}'] = df.loc[
        (min_date < df.date64) & (df.date64 <= max_date)
    ].copy()


7 commentaires

Mettre de côté la discussion pour savoir si c'est une bonne pratique ou non. - Je pense que c'est vraiment une mauvaise pratique, beaucoup mieux est dict


@Adrian ça marche? Je ne peux pas tester mon code maintenant.


@keepAlive jusqu'à présent, le code de votre solution ne fonctionne pas comme prévu, mais c'est peut-être à cause de moi .. J'ai inclus dans la question initiale comment j'ai essayé de mettre en œuvre votre solution.


@Adrian. C'est parce que lorsque vous faites pour élément dans dfs: print ("chaque élément", élément) , vous n'imprimez pas les éléments en tant que tels, mais leur clé associée dans le dictionnaire. Faites pour clé, élément dans dfs.items (): print ("chaque élément", clé, élément)


@keepAlive Oui, j'ai totalement oublié que vous devez itérer différemment sur les dictionnaires par rapport aux listes. Merci et désolé de vous avoir autant dérangé


Si ce que je propose fonctionne comme prévu, peut-être simplement modifier votre question pour refléter cela? :) ... Merci :)


@keepAlive Oui, je viens de faire ça: D



2
votes

Un meilleur moyen est d'utiliser les Period intégrés de Pandas :

df['year'] = df.date.dt.to_period('A-MAR')  # assigns years ending in March
dfs = [df.loc[df.year == year] for year in df.year.unique()]  
# gives list of dfs divided by year


5 commentaires

Votre code fonctionne comme prévu, mais il y a une petite erreur qui apparaît en haut: A: / python project / trailing_accounts / read_excel.py: 32: SettingWithCopyWarning: Une valeur essaie d'être définie sur une copie d'une tranche à partir d'un DataFrame. Essayez d'utiliser .loc [row_indexer, col_indexer] = value à la place Savez-vous ce qui pourrait en être la cause?


read_excel est le nom du fichier .py. Au départ, je travaillais avec Excel et non avec des CSV. Changé maintenant en read_csv pour correspondre au problème en cours. La ligne 32 est celle-ci: df_name ['year'] = df_name.date.dt.to_period ('A-MAR') J'ai changé le df en df_name car j'ai d'abord besoin de regrouper les données par nom puis faites le regroupement par année


est-ce réglé?


oui, le problème est réglé. Merci beaucoup pour votre temps.


Vous êtes le bienvenu :) Je pense que ma réponse serait plus rapide / plus idiomatique que d'utiliser apply comme l'a suggéré Jeril mais si les performances ne sont pas une préoccupation majeure, peu importe ce qui fonctionne ...