Code source de ensae_teaching_cs.faq.faq_pandas

# -*- coding: utf-8 -*-
"""
Quelques problèmes récurrents avec `pandas <http://pandas.pydata.org/>`_.


:githublink:`%|py|6`
"""


[docs]def read_csv(filepath_or_buffer, encoding="utf8", sep="\t", **args): """ Calls function `read_csv <http://pandas.pydata.org/pandas-docs/stable/generated/pandas.read_csv.html?highlight=read_csv#pandas.read_csv>`_ with different defaults values. If the encoding is utf8 and the data is a file name, the function checks there is no BOM at the beginning. Otherwise, it uses the encoding ``utf-8-sig``. :param encoding: encoding :param filepath_or_buffer: filepath_or_buffer :param sep: column separator :return: DataFrame .. faqref:: :tag: pandas :title: Caractères bizarres en utf8 et sous Windows (BOM) ? .. index:: encoding, BOM, UTF8 Sous Windows, certains logiciels comme `Notepad <http://fr.wikipedia.org/wiki/Bloc-notes_%28Windows%29>`_ permettent d'enregister un fichier sous différents `encodings <http://en.wikipedia.org/wiki/Character_encoding>`_. Avec l'encoding `UTF8 <http://fr.wikipedia.org/wiki/UTF-8>`_, on a parfois un problème avec le premier caractère ``\\ufeff`` car Notepad ajoute ce qu'on appelle un `BOM <http://fr.wikipedia.org/wiki/Indicateur_d%27ordre_des_octets>`_. Par exemple :: import pandas df = pandas.read_csv("dataframe.txt",sep="\\t", encoding="utf8") print(df) Provoque une erreur des plus énervantes :: UnicodeEncodeError: 'charmap' codec can't encode character '\\ufeff' in position 0: character maps to <undefined> Pour contrecarrer ceci, il suffit de modifier l'encoding par `utf-8-sig <https://docs.python.org/3/library/codecs.html#encodings-and-unicode>`_ :: import pandas df = pandas.read_csv("dataframe.txt",sep="\\t", encoding="utf-8-sig") print(df) :githublink:`%|py|45` """ import pandas if isinstance(filepath_or_buffer, str): if encoding in ["utf8", "utf-8"]: try: df = pandas.read_csv( filepath_or_buffer, encoding=encoding, sep=sep, **args) if df.columns[0].startswith("\ufeff"): raise UnicodeError( "'charmap' codec can't encode characters in position 0-1325: character maps to <undefined>") return df except UnicodeDecodeError: df = pandas.read_csv( filepath_or_buffer, encoding="utf-8-sig", sep=sep, **args) return df except UnicodeError: df = pandas.read_csv( filepath_or_buffer, encoding="utf-8-sig", sep=sep, **args) return df else: return pandas.read_csv( filepath_or_buffer, encoding=encoding, sep=sep, **args) else: return pandas.read_csv( filepath_or_buffer, encoding=encoding, sep=sep, **args)
[docs]def df_to_clipboard(df, **args): """ Copies a dataframe as *csv* text into the clipboard. :param df: dataframe :param args: additional parameters, such as *sep*, by default the separator *sep* is ``\\t`` for this function until it is defined otherwise It relies on method :epkg:`to_clipboard`_. .. faqref:: :title: Copier un dataframe dans le presse-papier - clipboard :tag: pandas Pour récupérer un dataframe dans Excel, on peut utiliser la méthode `to_excel <http://pandas.pydata.org/pandas-docs/stable/generated/pandas.DataFrame.to_excel.html>`_ puis ouvrir le fichier dans Excel ou le copier dans le presse-papier et le coller dans une feuille ouverte dans Excel. C'est l'objet de la méthode :epkg:`to_clipboard`:: df = pandas.DataFrame ( ... ) df.to_clipboard(sep="\\t") :githublink:`%|py|105` """ if "sep" in args: df.to_clipboard(**args) else: df.to_clipboard(sep="\t", **args)
[docs]def df_equal(df1, df2): """ Compares two dataframe and tells if they are equal. :param df1: first dataframe :param df2: second dataframe :return: boolean The function compare column one by one. It does not check the order of the columns is the same. It reorders the columns before doing the comparison. If you need more complex comparison, you can look into function :epkg:`assert_frame_equal`_. The function does not handle well NaN values because ``numpy.nan != numpy.nan`` is true. It also compares types: .. faqref:: :tag: pandas :title: Comment comparer deux dataframe? Ecrire ``df1 == df2`` ne compare pas deux dataframes entre deux car le sens n'est pas forcément le même pour tout le monde. Même si les valeurs sont les mêmes, est-ce l'ordre des colonnes est important ? Il faut le faire soi-même pour une comparaison spécifique à vos besoins. Le code ci-dessus compare d'abord les dimensions, ensuite compare l'ordre des colonnes puis enfin les valeurs :: if df1.shape != df2.shape: return False l1 = list(df1.columns) l2 = list(df2.columns) l1.sort() l2.sort() if l1 != l2: return False df1 = df1[l1] df2 = df2[l2] t = (df1 == df2).all() s = set(t) return False not in s Autres alternatives : * `equals <http://pandas.pydata.org/pandas-docs/stable/generated/pandas.DataFrame.equals.html>`_ * :epkg:`assert_frame_equal` :githublink:`%|py|161` """ if df1.shape != df2.shape: return False l1 = list(df1.columns) l2 = list(df2.columns) l1.sort() l2.sort() if l1 != l2: return False df1 = df1[l1] df2 = df2[l2] s = set((df1.dtypes == df2.dtypes)) if False in s: return False s = set((df1 == df2).all()) return False not in s
[docs]def groupby_topn(df, by_keys, sort_keys, ascending=True, n=1, as_index=True): """ Takes the top *n* rows per group. :param df: dataframe :param by_keys: rows will be grouped by these columns :param sort_keys: rows will be sorted by these columns :param ascending: parameter associated to sord function :param n: n in top *n* :param as_index: if False, remove the index after the group by :return: result .. faqref:: :tag: pandas :title: top n lignes avec pandas Grouper puis garder les premières observations de ce groupe est un problème classique. Il n'existe pas de meilleure façon de le faire, cela dépend du nombre d'obervations par groupe. Le moyen le plus simple de le faire avec pandas est : * grouper les lignes * trier les lignes dans chaque groupe * garder les premières lignes dans chaque groupe Ceci donne :: df.groupby(by_keys) .apply(lambda x: x.sort_values(sort_keys, ascending=ascending).head(head)) .reset_index(drop=True) La dernière instruction supprimer l'index ce qui donne au dataframe final la même structure que le dataframe initial. .. runpython:: :showcode: import pandas l = [ dict(k1="a", k2="b", v=4, i=1), dict(k1="a", k2="b", v=5, i=1), dict(k1="a", k2="b", v=4, i=2), dict(k1="b", k2="b", v=1, i=2), dict(k1="b", k2="b", v=1, i=3)] df = pandas.DataFrame(l) df.groupby(["k1", "k2"]).apply(lambda x: x.sort_values(["v", "i"], ascending=True).head(1)) print(df) :githublink:`%|py|225` """ res = df.groupby(by_keys).apply(lambda x: x.sort_values( sort_keys, ascending=ascending).head(n)) if not as_index: res = res.reset_index(drop=True) return res
[docs]def speed_dataframe(): """ .. faqref:: :tag: pandas :title: Comment créer un dataframe rapidement ? Le notebook :ref:`dataframematrixspeedrst` compare différentes manières de créer un `dataframe <http://pandas-docs.github.io/pandas-docs-travis/enhancingperf.html?highlight=dataframe>`_ ou un `array <http://docs.scipy.org/doc/numpy/reference/generated/numpy.array.html>`_. Quelques enseignemens : * Même si les données sont produites par un générateur, pandas les convertit en liste. * La création d'un array est plus rapide à partir d'un générateur plutôt que d'une liste. :githublink:`%|py|246` """ pass