如何对Python数据框进行单元测试?
我有一些函数,它们的输入和输出都是数据框。我几乎每个函数都是这样的。现在,如果我想对此进行单元测试,最好的方法是什么?为每个函数创建一个新的数据框(填充值)似乎有点费力?
您能否向我推荐任何材料?是否应该为这些函数编写单元测试?
如何对Python数据框进行单元测试?
我有一些函数,它们的输入和输出都是数据框。我几乎每个函数都是这样的。现在,如果我想对此进行单元测试,最好的方法是什么?为每个函数创建一个新的数据框(填充值)似乎有点费力?
您能否向我推荐任何材料?是否应该为这些函数编写单元测试?
虽然Pandas的测试函数主要用于内部测试,但NumPy包含了一组非常有用的测试函数,文档在这里:NumPy Test Support。
这些函数比较NumPy数组,但你可以使用values
属性获取底层的Pandas DataFrame数据。你可以定义一个简单的DataFrame并将你的函数返回值与期望结果进行比较。
你可以使用一组测试数据来测试多个函数。这样,你可以使用Pytest Fixtures一次性定义该DataFrame,并在多个测试中使用它。
关于资源,我发现这篇文章对于使用NumPy和Pandas进行测试非常有用。我还在PyCon Canada 2016做了一个关于数据分析测试的短暂演讲:自动化你的数据分析测试。
您可以使用Pandas测试函数:
它将更加灵活,可以以不同的方式比较您的结果和计算出的结果。
例如:
df1=pd.DataFrame({'a':[1,2,3,4,5]})
df2=pd.DataFrame({'a':[6,7,8,9,10]})
expected_res=pd.Series([7,9,11,13,15])
pd.testing.assert_series_equal((df1['a']+df2['a']),expected_res,check_names=False)
了解更多详情,参考此链接
如果您正在使用pytest,pandasSnapshot
会很有用。
# use with pytest
import pandas as pd
from snapshottest_ext.dataframe import PandasSnapshot
def test_format(snapshot):
df = pd.DataFrame([['a', 'b'], ['c', 'd']],
columns=['col 1', 'col 2'])
snapshot.assert_match(PandasSnapshot(df))
缺点之一是快照不再可读。(将内容存储为csv更易读,但存在问题。
附注:我是pytest快照扩展的作者。
你可以使用 snapshottest
并像这样做:
def test_something_works(snapshot): # snapshot is a pytest fixture from snapshottest
data_frame = calc_something_and_return_pandas_dataframe()
snapshot.assert_match(data_frame.to_csv(index=False), 'some_module_level_unique_name_for_the_snapshot')
这将创建一个快照文件夹,其中包含一个csv输出的文件,您可以使用--snapshot-update
在代码更改时更新。
它通过比较data_frame
变量与保存到磁盘的内容来工作。
值得一提的是,您的快照应该被检入源代码管理。
我认为创建用于单元测试的小型数据框并不难?
import pandas as pd
from nose.tools import assert_dict_equal
input_df = pd.DataFrame.from_dict({
'field_1': [some, values],
'field_2': [other, values]
})
expected = {
'result': [...]
}
assert_dict_equal(expected, my_func(input_df).to_dict(), "oops, there's a bug...")
to_dict()
- 这样我就可以得到一个 dict
,可以使用建议的 nose
方法与 expected
进行比较。 - rtkaletaexpected
只是一个示例 - 你需要根据自己的需求进行修改。看起来你在 expected 和 actual DataFrame 结构上存在不匹配的情况。似乎被测试的函数返回的 DataFrame 是 3 行 x 1 列?那么 expected 应该更像 expected = {<column_name>: {<first_row_index_value>: <first_row_value>, <second_row_index_value>: <second_row_value>, <third_row_index_value: <third_row_value>}}
。 - rtkaletapd.read_csv()
进行解析。您也可以从CSV文件中解析预期输出并进行比较,或者使用df.to_csv()
将其写出为CSV文件并进行比对。csv
文件可能会很烦人。如果她的代码结构正确,输入/期望的DataFrames应该相当小,因此可以轻松地即时构建? - rtkaletadf = pd.DataFrame({"col1": [1042, 2, 9, 6], "col2": [5, 2, 7, 6]})
pd.testing.assert_series_equal(df["col1"], df["col2"])
> ???
E AssertionError: Series are different
E
E Series values are different (50.0 %)
E [index]: [0, 1, 2, 3]
E [left]: [1042, 2, 9, 6]
E [right]: [5, 2, 7, 6]
import beavis
beavis.assert_pd_column_equality(df, "col1", "col2")
assert_frame_equal
也无法提供可读的错误消息。以下是使用beavis比较DataFrame相等性的方法。df1 = pd.DataFrame({'col1': [1, 2], 'col2': [3, 4]})
df2 = pd.DataFrame({'col1': [5, 2], 'col2': [3, 4]})
beavis.assert_pd_equality(df1, df2)
frame-fixtures Python包(我是其中的作者)旨在使单元测试或性能测试中“创建新数据帧(填充值)”变得容易。
例如,如果您想针对一个具有数字索引的浮点数和字符串数据帧进行测试,可以使用紧凑的字符串声明生成数据帧。
>>> ff.Fixture.to_frame('i(I,int)|v(float,str)|s(4,2)').to_pandas()
0 1
34715 1930.40 zaji
-3648 -1760.34 zJnC
91301 1857.34 zDdR
30205 1699.34 zuVU
>>> ff.Fixture.to_frame('i(I,int)|v(float,str)|s(8,3)').to_pandas()
0 1 2
34715 1930.40 zaji 694.30
-3648 -1760.34 zJnC -72.96
91301 1857.34 zDdR 1826.02
30205 1699.34 zuVU 604.10
54020 268.96 zKka 1080.40
129017 3511.58 zJXD 2580.34
35021 1175.36 zPAQ 700.42
166924 2925.68 zyps 3338.48