Par exemple, je voudrais affirmer que deux Pyspark DataFrame ont les mêmes données, mais simplement en utilisant ==
vérifie qu'ils sont le même objet. Dans l'idéal, j'aimerais également préciser si l'ordre compte ou non.
J'ai essayé d'écrire une fonction qui déclenche une AssertionError
mais qui ajoute beaucoup de bruit à la sortie pytest car elle montre le traçage de cette fonction.
L'autre pensée que j'ai eue était de me moquer de la méthode __eq__
des DataFrames, mais je ne suis pas convaincu que ce soit la bonne voie à suivre.
Modifier:
J'ai envisagé d'utiliser simplement une fonction qui renvoie vrai ou faux au lieu d'un opérateur, mais cela ne semble pas fonctionner avec pytest_assertrepr_compare
. Je ne connais pas assez bien le fonctionnement de ce hook, il est donc possible qu'il existe un moyen de l'utiliser avec une fonction au lieu d'un opérateur.
4 Réponses :
Pour faire une comparaison brute entre les valeurs des DataFrames (doit être dans l'ordre exact), vous pouvez faire quelque chose comme ceci:
def assert_frame_equal_with_sort(results, expected, keycolumns): results = results.reindex(sorted(results.columns), axis=1) expected = expected.reindex(sorted(expected.columns), axis=1) results_sorted = results.sort_values(by=keycolumns).reset_index(drop=True) expected_sorted = expected.sort_values(by=keycolumns).reset_index(drop=True) pd.testing.assert_frame_equal(results_sorted, expected_sorted) df1 = spark.createDataFrame([Row(a=1, b=2, c=3), Row(a=1, b=3, c=3)]) df2 = spark.createDataFrame([Row(a=1, b=3, c=3), Row(a=1, b=2, c=3)]) assert_frame_equal_with_sort(df1.toPandas(), df2.toPandas(), ['b'])
Si vous voulez spécifier par ordre, vous pouvez faire quelques transformations sur le pandas DataFrame pour trier par une colonne particulière en utilisant d'abord la fonction suivante:
import pandas as pd from pyspark.sql import Row df1 = spark.createDataFrame([Row(a=1, b=2, c=3), Row(a=1, b=3, c=3)]) df2 = spark.createDataFrame([Row(a=1, b=2, c=3), Row(a=1, b=3, c=3)]) pd.testing.assert_frame_equal(df1.toPandas(), df2.toPandas())
Je cherche davantage comment utiliser quelque chose comme ça avec pytest, j'ai déjà le code pour comparer les DataFrame
Vous pouvez utiliser l'un des hooks pytest, en particulier le pytest_assertrepr_compare . Là, vous pouvez définir ce que vous voulez comparer et comment, les documents sont également très bons et avec des exemples. Bonne chance. :)
Au départ, je pensais que c'était aussi la réponse, mais je crois comprendre que c'est simplement utilisé pour déterminer la façon dont les données sont représentées dans le journal une fois que l'appel assert
échoue, il n'a aucun contrôle sur la façon dont le assert
est géré.
Ma solution actuelle est d'utiliser un patch pour remplacer la méthode __eq__
de DataFrame. Voici un exemple avec Pandas car il est plus rapide à tester avec, l'idée devrait s'appliquer à n'importe quel objet.
class SortedDF(object): "Indicates that the order of data matters when comparing to another df" def __init__(self, df): self.df = df def __eq__(self, other): # Put logic for comparing df's including order of data here # Returning True for demonstration purposes return True def test_sorted_df(): df1 = pd.DataFrame( {"id": [1, 2, 3], "name": ["a", "b", "c"]}, columns=["id", "name"] ) df2 = pd.DataFrame( {"id": [2, 3, 4], "name": ["b", "c", "d"]}, columns=["id", "name"] ) # Passes because SortedDF.__eq__ is used assert SortedDF(df1) == df2 # Fails because df2's __eq__ method is used assert df2 == SortedDF(df2)
Je ne l'ai pas encore essayé mais je prévois de l'ajouter comme appareil et d'utiliser autouse
pour l'utiliser automatiquement pour tous les tests.
Afin de gérer avec élégance l'indicateur "order Matters", je joue avec une approche similaire à pytest.approx
qui renvoie une nouvelle classe avec son propre __eq__
par exemple:
import pandas as pd # use this import for python3 # from unittest.mock import patch from mock import patch def custom_df_compare(self, other): # Put logic for comparing df's here # Returning True for demonstration return True @patch("pandas.DataFrame.__eq__", custom_df_compare) def test_df_equal(): df1 = pd.DataFrame( {"id": [1, 2, 3], "name": ["a", "b", "c"]}, columns=["id", "name"] ) df2 = pd.DataFrame( {"id": [2, 3, 4], "name": ["b", "c", "d"]}, columns=["id", "name"] ) assert df1 == df2
Le problème mineur que je n'ai pas pu résoudre est l'échec de la deuxième assertion, assert df2 == SortedDF ( df2)
. Cet ordre fonctionne bien avec pytest.approx
mais pas ici. J'ai essayé de lire sur l'opérateur ==
mais je n'ai pas été en mesure de trouver comment résoudre le deuxième cas.
utilisez simplement la méthode pandas.Dataframe.equals https://pandas.pydata.org/ pandas-docs / stable / reference / api / pandas.DataFrame.equals.html
Par exemple
assert df1.equals(df2)
assert peut être utilisé avec tout ce qui renvoie un booléen. Donc oui, vous pouvez écrire n'importe quelle fonction de comparaison personnalisée pour comparer deux objets. Tant que la fonction personnalisée renvoie un booléen. Cependant, dans ce cas, il n'y a pas besoin d'une fonction personnalisée car pandas en fournit déjà une
Le problème avec cette approche est qu'elle ne semble pas fonctionner avec pytest_assertrepr_compare
dont j'aimerais également profiter. Cela agit comme un hook qui reçoit l'opérateur, les éléments gauche et droit et vous permet de définir comment l'échec doit apparaître dans le journal. J'ajouterai ce détail à ma question.
Il semble que ce ne soit pas aussi simple qu'il y paraît. lien
Je suis d'accord pour comparer les deux DataFrames, ma question est de savoir comment utiliser cette logique de comparaison dans une assertion pytest
Si vous voulez faire ce test spécifique, vous pouvez créer une fonction qui renvoie True ou False pour la comparaison que vous souhaitez faire, puis faire une simple assertion IsTrue? Quelque chose comme ca. Je connais unittest et je n'ai pas essayé pytest, mais ça doit être la même logique, non?