====== 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()