====== Pandas ====== * [[https://berthub.eu/articles/posts/from-gnuplot-to-matplotlib-pandas/]] ==== Informations sur un dataframe ==== Le haut du dataframe : df.head() Le bas du dataframe : df.tail() Les colonnes dans un dataframe : df.columns Afficher tout le dataframe ([[https://www.geeksforgeeks.org/how-to-print-an-entire-pandas-dataframe-in-python/|source]]) : print(df.to_markdown()) # il faut installer le paquet tabulate / python3-tabulate ou bien : pd.set_option("display.max_columns", None) pd.set_option("display.max_rows", None) pd.set_option("display.width", None) Pour afficher le dataframe en arrondissant : print(df.round(2)) ==== Lire des fichiers ==== * [[https://www.statology.org/pandas-read-text-file/]] * https://pandas.pydata.org/docs/reference/api/pandas.read_csv.html Avoir le nom des colonnes, lorsque la ligne qui les contient est considérée comme un commentaire: h = pd.read_csv(f, skiprows=6, nrows=1, delim_whitespace=True, header=None) df = pd.read_csv(f, delim_whitespace=True, comment="#", header=None, names=list(h.loc[0,1:])) Si les deux premières lignes servent d'en-tête: ''header=[0, 1]''; on peut ensuite accéder aux colonnes avec ''df[("level0", "level1")]''. On peut préciser le séparateur avec le paramètre ''sep''. Le paramètre ''usecols'' prend une liste de noms ou index de colonnes à conserver dans le dataframe. ==== Filtrer les données ==== to_plot = data[f][(data[f]['n.nodes']==max_nb_nodes) & (data[f]['length']>=1024)] cholesky = df[~(df['Function']=="FxT")] fxt = df[df['Function']=="FxT"] Pour filtrer suivant si un élément est dans un ensemble ([[https://stackoverflow.com/questions/19960077/how-to-filter-pandas-dataframe-using-in-and-not-in-like-in-sql|source]]) : df = df[~df["Name"].isin(["a", "b", "c", "d"])] Pour filtrer les valeurs ''None'' ([[https://stackoverflow.com/questions/45117272/pandas-filtering-none-values|source]]) : df = df[df["Column"].isnull()] Pour filtrer avec une fonction : df_all = df[df["Parameter filename"].apply(lambda x: not x.startswith("a"))] === Supprimer les lignes avec des données manquantes === * https://pandas.pydata.org/docs/reference/api/pandas.DataFrame.dropna.html df.dropna() On peut préciser dans quelles colonnes regarder s'il y des valeurs nulles : df.dropna(subset=['Name']) === Changer les valeurs de données filtrées === [[https://stackoverflow.com/questions/38876816/change-value-of-a-dataframe-column-based-on-a-filter|Source]] df.loc[df["Column A"]=="foo", "Column B"] = "bar" ==== Appliquer une fonction sur une colonne === [[https://stackoverflow.com/questions/42529454/using-map-for-columns-in-a-pandas-dataframe|Source]] df['name'] = df['name'].apply(lambda s: s.replace("_STARPU_", "").replace("FUT_", "")) ==== Minimum ligne par ligne de deux colonnes ==== [[https://stackoverflow.com/questions/16989946/creating-an-element-wise-minimum-series-from-two-other-series-in-python-pandas|Source]] pandas.concat([s1, s2], axis=1).min(axis=1) ==== Trier ==== to_plot = df.sort_values(by=['name']) S'il y a plusieurs niveaux de colonnes, il faut utiliser des tuples : ''by=[('Percentage', 'mean')]''. ''ascending=False'' pour avoir un tri décroissant. ==== Agréger ==== stats = df.groupby('n').agg({"gflops": ["median", "min", "max"]}) print(stats['gflops']['median']) # S'il n'y a qu'une seule valeur: print(stats['gflops']['median'].values[0]) On peut agréger sur plusieurs colonnes : df.groupby(['n', 'm']) Pour récupérer les valeurs des indexes (les valeurs sur lesquelles se font l'agrégation) : # agrégation sur une valeur : s.index # agrégation sur plusieurs valeurs : s.index.get_level_index(0) # première valeur s.index.get_level_index(1) # deuxième valeur Pour n'avoir qu'un niveau d'en-tête ([[https://stackoverflow.com/questions/14507794/how-to-flatten-a-hierarchical-index-in-columns/55757002|source]]) : df = df.groupby(["Parameter filename", "Comm size", "Msg size"]).agg({"Collective duration": ["max"]}).reset_index() df.columns = [i for i, _ in df.columns] # ou : df.columns = [' '.join(col).strip() for col in df.columns.values] Liste des fonctions d’agrégation possibles: [[https://cmdlinetips.com/2019/10/pandas-groupby-13-functions-to-aggregate/]] Obtenir la première ligne de chaque groupe : df.group("column").first() === Déciles === [[https://stackoverflow.com/a/54593214|Source]] def percentile(n): def percentile_(x): return x.quantile(n) percentile_.__name__ = 'percentile_{:2.0f}'.format(n*100) return percentile_ stats = df.groupby('n').agg({"gflops": ["median", percentile(0.1)]}) print(stats['gflops']['percentile_10']) === Grouper en fusionnant les contenus des cellules === [[https://stackoverflow.com/questions/36392735/how-to-combine-multiple-rows-into-a-single-row-with-pandas|Source]] # On groupe par le contenu de la colonne A, puis les différents contenus des cellules dans la colonne B de chaque groupe sont concaténés dans une seule cellule, ce qui donne une ligne par groupe : df.groupby("Column A")["Column B"].apply(" ".join).reset_index() ==== Concaténer ==== Pour concaténer des dataframes ([[https://pandas.pydata.org/pandas-docs/stable/user_guide/merging.html#__do_not_save__|source]]): result = pd.concat([df1, df2]) Pour concaténer les colonnes : df = pd.concat([df_a, df_b], axis=1, join="inner") Il faut être sûr que les indexes soient les mêmes dans les deux dataframes. L'utilisation de ''join="inner"'' permet de ne pas garder les lignes avec des indexes qui ne correspondent pas dans les deux dataframes. ==== Changer la colonne d'index ==== Pour changer la colonne qui sert d'index df.set_index("colonne", inplace=True) Il possible de fournir une liste de colonnes. ==== Supprimer des colonnes ==== # UPI2 est une sous-colonne de la colonne "System": df.drop(columns=["System"], level=0, inplace=True) df.drop(columns=["UPI2"], level=1, inplace=True) Supprimer la dernière colonne: df.drop(df.columns[len(df.columns)-1], axis=1, inplace=True) ==== Renommer des colonnes ==== df_starts.rename(columns={"Time start": "Time"}, inplace=True) Pour des colonnes qui n'avaient initialement pas de nom : df.columns = ["A", "B", "C"] ==== Éclater une colonne textuelle en plusieurs colonnes ==== [[https://stackoverflow.com/questions/14745022/how-to-split-a-dataframe-string-column-into-two-columns|Source]] Si les valeurs de la colonne ''Configuration'' sont de la forme ''baseline.C.openmpi.linear.1'' : df[['Type', 'Size', 'MPI', 'Coll', 'Iter']] = df["Configuration"].str.split('.', expand=True) ==== Valeurs uniques ==== Obtenir les valeurs uniques d'une colonne : print(df["Comm ID"].sort_values().unique() Obtenir les valeurs uniques d'une colonne et le nombre d’occurrences : print(df[["WorkerId"]].value_counts()) Obtenir les couples uniques de deux colonnes : threads = final_df[["Thread", "Core"]].value_counts().reset_index(name='count') threads.drop(columns=["count"], inplace=True) On peut aussi simplement utiliser ([[https://stackoverflow.com/questions/47364539/get-unique-pairs-of-columns-of-a-pandas-dataframe|source]]) : df[["Operation", "Datasize"]].drop_duplicates() ==== Convertir un dataframe en dictionnaire ==== S'il n'a que deux colonnes, ''Thread'' et ''Core'': threads.set_index("Thread", inplace=True) binding_thread_to_core = threads.to_dict()['Core'] ==== Récupérer les données d'un dataframe ==== === Itérer sur les lignes d'un dataframe === for index, row in df.iterrows(): print(row['column_a']) === Récupérer la valeur d'une cellule en particulier === value = df.at[0, "P"] # [numéro de ligne, nom de colonne] === Convertir en dictionnaire une unique ligne === assert(len(row) == 1) row = row.squeeze().to_dict() ==== Transformations sur les dataframes ==== === Sorte de transposées === Avec le dataframe suivant : Rang Operation Valeur 0 A 12 0 B 13 1 A 14 1 B 15 Alors le code df.pivot(index="Rang", columns="Operation", values="Valeur") donnera le dataframe suivant : Rang A B 0 12 13 1 14 15 ==== Fonction inverse d'un percentile ==== Pour connaître quelle est la proportion de valeurs qui sont inférieures à ''x'' ([[https://stackoverflow.com/questions/26489134/whats-the-inverse-of-the-quantile-function-on-a-pandas-series|source]]) : (df < x).astype(int).mean()