[{"data":1,"prerenderedAt":940},["ShallowReactive",2],{"page-\u002Fproperty-based-fuzz-testing-strategies\u002Fdesigning-strategies-for-domain-data\u002Fgenerating-dataframes-and-arrays-with-hypothesis-extras\u002F":3},{"id":4,"title":5,"body":6,"description":903,"extension":904,"meta":905,"navigation":106,"path":936,"seo":937,"stem":938,"__hash__":939},"content\u002Fproperty-based-fuzz-testing-strategies\u002Fdesigning-strategies-for-domain-data\u002Fgenerating-dataframes-and-arrays-with-hypothesis-extras\u002Findex.md","Generating DataFrames and Arrays with Hypothesis Extras",{"type":7,"value":8,"toc":892},"minimark",[9,22,25,30,56,60,282,410,414,420,434,438,491,495,498,512,533,546,569,657,661,664,674,677,787,791,794,801,804,811,815,828,837,851,855,883,888],[10,11,12,13,17,18,21],"p",{},"Numerical code breaks on inputs nobody writes by hand: an empty array, a single row, a column of identical values, a float that is exactly representable in one dtype and not another, a NaN in the one position the algorithm did not expect. Example-based tests cover the shapes the author thought of; Hypothesis's ",[14,15,16],"code",{},"extra.numpy"," and ",[14,19,20],{},"extra.pandas"," modules generate arrays and frames across shapes, dtypes and element values, and then shrink any failure to the smallest one that still breaks.",[10,23,24],{},"The strategies are declarative — dtype, shape bounds, element strategy — and most of the skill is in constraining them to the inputs the code is supposed to handle while leaving enough variety to find the ones it mishandles. A deliberate policy on NaN and infinity, chosen per property, is the single most important decision. Get it wrong in one direction and every property has to hedge against values that make it meaningless; get it wrong in the other and the suite never sees the missing values real data is full of.",[26,27,29],"h2",{"id":28},"prerequisites","Prerequisites",[31,32,33,40,49],"ul",{},[34,35,36,39],"li",{},[14,37,38],{},"hypothesis[numpy,pandas] >= 6.100",", NumPy and pandas.",[34,41,42,43,48],{},"The strategy-design principles from ",[44,45,47],"a",{"href":46},"\u002Fproperty-based-fuzz-testing-strategies\u002Fdesigning-strategies-for-domain-data\u002F","designing strategies for domain data",".",[34,50,51,52,48],{},"The float edge cases in ",[44,53,55],{"href":54},"\u002Fproperty-based-fuzz-testing-strategies\u002Fadvanced-property-based-testing\u002Ftesting-numeric-code-with-floats-and-nan-edge-cases\u002F","testing numeric code with floats and NaN edge cases",[26,57,59],{"id":58},"solution","Solution",[61,62,67],"pre",{"className":63,"code":64,"language":65,"meta":66,"style":66},"language-python shiki shiki-themes github-light github-dark","import numpy as np\nimport pandas as pd\nfrom hypothesis import given, settings, strategies as st\nfrom hypothesis.extra import numpy as npst\nfrom hypothesis.extra import pandas as pdst\n\nfinite = st.floats(min_value=-1e6, max_value=1e6, allow_nan=False, allow_infinity=False)\n\n\n@given(npst.arrays(dtype=np.float64,\n                   shape=npst.array_shapes(min_dims=1, max_dims=2, max_side=20),\n                   elements=finite))\ndef test_normalise_preserves_shape_and_is_bounded(arr):\n    out = normalise(arr)\n    assert out.shape == arr.shape\n    assert np.all((out >= 0.0) & (out \u003C= 1.0) | np.isnan(out))\n\n\norders = pdst.data_frames(\n    columns=[\n        pdst.column(\"order_id\", dtype=str, elements=st.text(min_size=1, max_size=8),\n                    unique=True),\n        pdst.column(\"amount_minor\", dtype=np.int64,\n                    elements=st.integers(min_value=0, max_value=10_000_000)),\n        pdst.column(\"currency\", elements=st.sampled_from([\"GBP\", \"USD\", \"JPY\"])),\n    ],\n    index=pdst.range_indexes(min_size=0, max_size=30),   # includes the empty frame\n)\n\n\n@settings(max_examples=150)\n@given(orders)\ndef test_totals_by_currency_match_a_plain_python_sum(df):\n    result = totals_by_currency(df)\n    for currency, group in df.groupby(\"currency\"):\n        assert result[currency] == int(group[\"amount_minor\"].sum())\n","python","",[14,68,69,77,83,89,95,101,108,114,119,124,130,136,142,148,154,160,166,171,176,182,188,194,200,206,212,218,224,230,236,241,246,252,258,264,270,276],{"__ignoreMap":66},[70,71,74],"span",{"class":72,"line":73},"line",1,[70,75,76],{},"import numpy as np\n",[70,78,80],{"class":72,"line":79},2,[70,81,82],{},"import pandas as pd\n",[70,84,86],{"class":72,"line":85},3,[70,87,88],{},"from hypothesis import given, settings, strategies as st\n",[70,90,92],{"class":72,"line":91},4,[70,93,94],{},"from hypothesis.extra import numpy as npst\n",[70,96,98],{"class":72,"line":97},5,[70,99,100],{},"from hypothesis.extra import pandas as pdst\n",[70,102,104],{"class":72,"line":103},6,[70,105,107],{"emptyLinePlaceholder":106},true,"\n",[70,109,111],{"class":72,"line":110},7,[70,112,113],{},"finite = st.floats(min_value=-1e6, max_value=1e6, allow_nan=False, allow_infinity=False)\n",[70,115,117],{"class":72,"line":116},8,[70,118,107],{"emptyLinePlaceholder":106},[70,120,122],{"class":72,"line":121},9,[70,123,107],{"emptyLinePlaceholder":106},[70,125,127],{"class":72,"line":126},10,[70,128,129],{},"@given(npst.arrays(dtype=np.float64,\n",[70,131,133],{"class":72,"line":132},11,[70,134,135],{},"                   shape=npst.array_shapes(min_dims=1, max_dims=2, max_side=20),\n",[70,137,139],{"class":72,"line":138},12,[70,140,141],{},"                   elements=finite))\n",[70,143,145],{"class":72,"line":144},13,[70,146,147],{},"def test_normalise_preserves_shape_and_is_bounded(arr):\n",[70,149,151],{"class":72,"line":150},14,[70,152,153],{},"    out = normalise(arr)\n",[70,155,157],{"class":72,"line":156},15,[70,158,159],{},"    assert out.shape == arr.shape\n",[70,161,163],{"class":72,"line":162},16,[70,164,165],{},"    assert np.all((out >= 0.0) & (out \u003C= 1.0) | np.isnan(out))\n",[70,167,169],{"class":72,"line":168},17,[70,170,107],{"emptyLinePlaceholder":106},[70,172,174],{"class":72,"line":173},18,[70,175,107],{"emptyLinePlaceholder":106},[70,177,179],{"class":72,"line":178},19,[70,180,181],{},"orders = pdst.data_frames(\n",[70,183,185],{"class":72,"line":184},20,[70,186,187],{},"    columns=[\n",[70,189,191],{"class":72,"line":190},21,[70,192,193],{},"        pdst.column(\"order_id\", dtype=str, elements=st.text(min_size=1, max_size=8),\n",[70,195,197],{"class":72,"line":196},22,[70,198,199],{},"                    unique=True),\n",[70,201,203],{"class":72,"line":202},23,[70,204,205],{},"        pdst.column(\"amount_minor\", dtype=np.int64,\n",[70,207,209],{"class":72,"line":208},24,[70,210,211],{},"                    elements=st.integers(min_value=0, max_value=10_000_000)),\n",[70,213,215],{"class":72,"line":214},25,[70,216,217],{},"        pdst.column(\"currency\", elements=st.sampled_from([\"GBP\", \"USD\", \"JPY\"])),\n",[70,219,221],{"class":72,"line":220},26,[70,222,223],{},"    ],\n",[70,225,227],{"class":72,"line":226},27,[70,228,229],{},"    index=pdst.range_indexes(min_size=0, max_size=30),   # includes the empty frame\n",[70,231,233],{"class":72,"line":232},28,[70,234,235],{},")\n",[70,237,239],{"class":72,"line":238},29,[70,240,107],{"emptyLinePlaceholder":106},[70,242,244],{"class":72,"line":243},30,[70,245,107],{"emptyLinePlaceholder":106},[70,247,249],{"class":72,"line":248},31,[70,250,251],{},"@settings(max_examples=150)\n",[70,253,255],{"class":72,"line":254},32,[70,256,257],{},"@given(orders)\n",[70,259,261],{"class":72,"line":260},33,[70,262,263],{},"def test_totals_by_currency_match_a_plain_python_sum(df):\n",[70,265,267],{"class":72,"line":266},34,[70,268,269],{},"    result = totals_by_currency(df)\n",[70,271,273],{"class":72,"line":272},35,[70,274,275],{},"    for currency, group in df.groupby(\"currency\"):\n",[70,277,279],{"class":72,"line":278},36,[70,280,281],{},"        assert result[currency] == int(group[\"amount_minor\"].sum())\n",[283,284,287,406],"figure",{"className":285},[286],"diagram",[288,289,296,297,296,301,296,305,296,313,296,323,296,332,296,335,296,340,296,346,296,350,296,354,296,359,296,363,296,365,296,368,296,372,296,375,296,378,296,382,296,386,296,388,296,392,296,396,296,399,296,402],"svg",{"viewBox":290,"role":291,"ariaLabelledBy":292,"xmlns":295},"0 0 820 262","img",[293,294],"np-t","np-d","http:\u002F\u002Fwww.w3.org\u002F2000\u002Fsvg","\n  ",[298,299,300],"title",{"id":293},"The three decisions in an array strategy",[302,303,304],"desc",{"id":294},"An array strategy is built from three choices. The dtype decides representable values and overflow behaviour. The shape strategy bounds the number of dimensions and the side length, including empty and single-element arrays. The element strategy bounds values and sets the policy for NaN and infinity. Together they define the input space the property is claimed over.",[306,307],"rect",{"x":308,"y":308,"width":309,"height":310,"rx":311,"fill":312},"0","820","262","14","#fffdf8",[314,315,322],"text",{"x":316,"y":317,"textAnchor":318,"fontSize":319,"fontWeight":320,"fill":321},"410","28","middle","16","700","#3d405b","dtype × shape × elements = the claimed input space",[306,324],{"x":325,"y":326,"width":327,"height":328,"rx":329,"fill":312,"stroke":330,"strokeWidth":331},"24","52","250","186","12","#81b29a","2",[306,333],{"x":325,"y":326,"width":327,"height":334,"rx":329,"fill":321},"30",[314,336,339],{"x":337,"y":338,"textAnchor":318,"fontSize":329,"fontWeight":320,"fill":312},"149","72","dtype",[314,341,345],{"x":342,"y":343,"fontSize":344,"fill":321},"40","108","11","float64, float32, int64 …",[314,347,349],{"x":342,"y":348,"fontSize":344,"fill":321},"130","decides precision and",[314,351,353],{"x":342,"y":352,"fontSize":344,"fill":321},"150","overflow behaviour",[314,355,358],{"x":342,"y":356,"fontSize":344,"fill":357},"194","#2a5f49","match production data",[306,360],{"x":361,"y":326,"width":327,"height":328,"rx":329,"fill":312,"stroke":362,"strokeWidth":331},"285","#f2cc8f",[306,364],{"x":361,"y":326,"width":327,"height":334,"rx":329,"fill":321},[314,366,367],{"x":316,"y":338,"textAnchor":318,"fontSize":329,"fontWeight":320,"fill":312},"shape",[314,369,371],{"x":370,"y":343,"fontSize":344,"fill":321},"301","min_dims, max_dims,",[314,373,374],{"x":370,"y":348,"fontSize":344,"fill":321},"max_side — includes empty",[314,376,377],{"x":370,"y":352,"fontSize":344,"fill":321},"and single-element arrays",[314,379,381],{"x":370,"y":356,"fontSize":344,"fill":380},"#8a5a00","keep sides small",[306,383],{"x":384,"y":326,"width":327,"height":328,"rx":329,"fill":312,"stroke":385,"strokeWidth":331},"546","#e07a5f",[306,387],{"x":384,"y":326,"width":327,"height":334,"rx":329,"fill":321},[314,389,391],{"x":390,"y":338,"textAnchor":318,"fontSize":329,"fontWeight":320,"fill":312},"671","elements",[314,393,395],{"x":394,"y":343,"fontSize":344,"fill":321},"562","bounds, and a deliberate",[314,397,398],{"x":394,"y":348,"fontSize":344,"fill":321},"NaN \u002F infinity policy",[314,400,401],{"x":394,"y":352,"fontSize":344,"fill":321},"per property",[314,403,405],{"x":394,"y":356,"fontSize":344,"fill":404},"#8f3d22","the decision that matters most",[407,408,409],"figcaption",{},"Each choice is a claim about what the code accepts. Making them explicit in the strategy documents the function's contract as well as testing it.",[26,411,413],{"id":412},"why-this-works","Why this works",[10,415,416,419],{},[14,417,418],{},"npst.arrays"," draws a shape from the shape strategy, then fills an array of that shape with values from the element strategy, cast to the dtype. Because shape and elements are both strategies, shrinking reduces both: a failure on a 17×9 array of large floats shrinks to the smallest shape and simplest values that still fail — often a 1×1 array containing zero, or an empty array, which points straight at the bug.",[10,421,422,425,426,429,430,433],{},[14,423,424],{},"pdst.data_frames"," does the same per column and builds the frame through pandas, so the result has real dtypes and a real index. Declaring columns explicitly makes generated frames schema-valid by construction, and ",[14,427,428],{},"range_indexes(min_size=0)"," guarantees the empty frame is among the inputs — the case most ",[14,431,432],{},"groupby"," and aggregation code gets wrong first.",[26,435,437],{"id":436},"edge-cases-and-failure-modes","Edge cases and failure modes",[31,439,440,455,461,471,477],{},[34,441,442,446,447,450,451,454],{},[443,444,445],"strong",{},"Unbounded floats."," ",[14,448,449],{},"st.floats()"," includes values near ",[14,452,453],{},"1e308"," whose sums overflow to infinity. Bound them to the range the code handles.",[34,456,457,460],{},[443,458,459],{},"NaN excluded everywhere."," Excluding NaN in every strategy hides the bugs where real data contains it. Keep at least one property that allows it.",[34,462,463,466,467,470],{},[443,464,465],{},"Large frames."," Big ",[14,468,469],{},"max_size"," values make each example and every shrink step slow. Twenty or thirty rows find nearly all bugs.",[34,472,473,476],{},[443,474,475],{},"Object dtypes."," Columns without a dtype default to object, which pandas treats differently from typed columns. Give every column an explicit dtype matching production.",[34,478,479,482,483,486,487,490],{},[443,480,481],{},"Comparing floats exactly."," A reference computed in a different order can differ in the last bit. Use ",[14,484,485],{},"np.allclose"," or ",[14,488,489],{},"math.isclose"," with a tolerance justified by the algorithm.",[26,492,494],{"id":493},"dtype-specific-surprises-generation-finds","Dtype-specific surprises generation finds",[10,496,497],{},"A large share of the bugs these strategies find are not logic errors but dtype behaviour the author did not expect, and knowing the usual suspects makes the shrunk counterexamples quicker to read.",[10,499,500,503,504,507,508,511],{},[443,501,502],{},"Integer overflow without an error."," NumPy integer arithmetic wraps silently: ",[14,505,506],{},"np.int32(2**31 - 1) + 1"," is a large negative number, not an exception. A sum over a generated ",[14,509,510],{},"int32"," column with large values produces a negative total, and the counterexample is typically two elements near the dtype's maximum. The fix is an explicit wider dtype for the accumulation, and the test is what reveals that one is needed.",[10,513,514,517,518,521,522,525,526,528,529,532],{},[443,515,516],{},"Float precision across dtypes."," Values exactly representable in ",[14,519,520],{},"float64"," may round in ",[14,523,524],{},"float32",", so a round trip through a ",[14,527,524],{}," column changes them. Properties comparing before and after a cast need a tolerance, or a strategy restricted to values representable in the narrower type — ",[14,530,531],{},"st.floats(width=32)"," generates exactly those.",[10,534,535,446,538,541,542,545],{},[443,536,537],{},"Signed zero and NaN comparisons.",[14,539,540],{},"-0.0 == 0.0"," is true but they sort and hash differently in some contexts; ",[14,543,544],{},"NaN != NaN"," breaks any equality-based deduplication. A generated column containing both zeros or a NaN will find every place that assumes otherwise.",[10,547,548,446,551,554,555,558,559,561,562,565,566,48],{},[443,549,550],{},"Nullable versus NumPy dtypes in pandas.",[14,552,553],{},"Int64"," with capital I supports missing values, ",[14,556,557],{},"int64"," does not, and a column that acquires a missing value is silently upcast to ",[14,560,520],{},". Generating frames with an occasional ",[14,563,564],{},"None"," in an integer column exposes the upcast, which usually shows up downstream as a comparison failing on ",[14,567,568],{},"1.0 != 1",[283,570,572,654],{"className":571},[286],[288,573,296,578,296,581,296,584,296,588,296,593,296,599,296,604,296,608,296,612,296,616,296,620,296,623,296,626,296,630,296,634,296,637,296,641,296,645,296,648,296,651],{"viewBox":574,"role":291,"ariaLabelledBy":575,"xmlns":295},"0 0 800 236",[576,577],"dt-t","dt-d",[298,579,580],{"id":576},"Dtype behaviours generated inputs expose",[302,582,583],{"id":577},"Four cards. Integer overflow wraps silently to a negative value. Float32 rounding changes values that were exact in float64. Signed zero and NaN break equality-based logic. Nullable pandas integers upcast to float when a missing value appears. Each is found by generation and typically shrinks to a two-element example.",[306,585],{"x":308,"y":308,"width":586,"height":587,"rx":311,"fill":312},"800","236",[314,589,592],{"x":590,"y":317,"textAnchor":318,"fontSize":591,"fontWeight":320,"fill":321},"400","15.5","Most shrunk counterexamples are one of these",[306,594],{"x":325,"y":595,"width":596,"height":597,"rx":344,"fill":598,"stroke":385,"strokeWidth":331},"50","370","78","#fbe9e3",[314,600,603],{"x":601,"y":602,"fontSize":329,"fontWeight":320,"fill":321},"44","76","silent integer overflow",[314,605,607],{"x":601,"y":606,"fontSize":344,"fill":321},"98","int32 max + 1 → large negative",[314,609,611],{"x":601,"y":610,"fontSize":344,"fill":404},"116","accumulate in a wider dtype",[306,613],{"x":614,"y":595,"width":596,"height":597,"rx":344,"fill":615,"stroke":362,"strokeWidth":331},"406","#f7f0da",[314,617,619],{"x":618,"y":602,"fontSize":329,"fontWeight":320,"fill":321},"426","float32 rounding",[314,621,622],{"x":618,"y":606,"fontSize":344,"fill":321},"exact in float64, not in float32",[314,624,625],{"x":618,"y":610,"fontSize":344,"fill":380},"st.floats(width=32) or a tolerance",[306,627],{"x":325,"y":628,"width":596,"height":597,"rx":344,"fill":629,"stroke":330,"strokeWidth":331},"138","#e6f0ea",[314,631,633],{"x":601,"y":632,"fontSize":329,"fontWeight":320,"fill":321},"164","-0.0 and NaN",[314,635,636],{"x":601,"y":328,"fontSize":344,"fill":321},"NaN != NaN breaks dedup",[314,638,640],{"x":601,"y":639,"fontSize":344,"fill":357},"204","use isnan-aware comparisons",[306,642],{"x":614,"y":628,"width":596,"height":597,"rx":344,"fill":643,"stroke":321,"strokeWidth":644},"#f4f1de","1.6",[314,646,647],{"x":618,"y":632,"fontSize":329,"fontWeight":320,"fill":321},"nullable int upcast",[314,649,650],{"x":618,"y":328,"fontSize":344,"fill":321},"int64 + missing → float64",[314,652,653],{"x":618,"y":639,"fontSize":344,"fill":321},"use Int64 or handle NaN",[407,655,656],{},"Recognising the pattern in a two-element counterexample usually identifies the fix before any debugging starts.",[26,658,660],{"id":659},"reference-implementations-as-the-property","Reference implementations as the property",[10,662,663],{},"The hardest part of property-testing numerical code is stating a property at all — \"the output is correct\" is not checkable without an oracle. The most productive oracle is a slow, obvious implementation of the same computation written in plain Python, with the vectorised or optimised version tested for agreement against it on generated inputs.",[10,665,666,667,670,671,673],{},"That is the pattern in the DataFrame test above: ",[14,668,669],{},"totals_by_currency"," might use a vectorised ",[14,672,432],{}," with categorical dtypes and a pre-sorted index, while the check uses a loop over groups that nobody could get wrong. Any disagreement is a bug in the fast version — or occasionally a precision question worth understanding — and Hypothesis shrinks it to the smallest frame that disagrees, typically two or three rows that make the cause obvious.",[10,675,676],{},"The reference need not be efficient, and should not be clever. Its only job is to be obviously correct for small inputs, which is exactly the size Hypothesis generates and shrinks to. The same idea applies to any optimised numerical routine: a moving average against an explicit window loop, a matrix operation against nested loops, a custom aggregation against its textbook definition. It is the property-based equivalent of a golden reference, without the need to store golden outputs.",[283,678,680,784],{"className":679},[286],[288,681,296,685,296,688,296,691,296,708,296,710,296,713,296,720,296,725,296,729,296,736,296,740,296,745,296,751,296,756,296,759,296,762,296,766,296,770,296,773,296,776,296,781],{"viewBox":574,"role":291,"ariaLabelledBy":682,"xmlns":295},[683,684],"ref-t","ref-d",[298,686,687],{"id":683},"Testing an optimised routine against a plain reference",[302,689,690],{"id":684},"Generated frames are fed to both the optimised vectorised implementation and a slow plain Python reference. Their results are compared, and any disagreement is shrunk by Hypothesis to the smallest frame that still disagrees, usually a handful of rows that reveal the bug.",[692,693,694,695,296],"defs",{},"\n    ",[696,697,704],"marker",{"id":698,"viewBox":699,"refX":700,"refY":701,"markerWidth":702,"markerHeight":702,"orient":703},"ref-a","0 0 10 10","9","5","7","auto-start-reverse",[705,706],"path",{"d":707,"fill":321},"M0 0 L10 5 L0 10 z",[306,709],{"x":308,"y":308,"width":586,"height":587,"rx":311,"fill":312},[314,711,712],{"x":590,"y":317,"textAnchor":318,"fontSize":591,"fontWeight":320,"fill":321},"The obvious version is the oracle",[306,714],{"x":715,"y":716,"width":717,"height":718,"rx":719,"fill":615,"stroke":362,"strokeWidth":331},"26","94","170","60","10",[314,721,724],{"x":722,"y":723,"textAnchor":318,"fontSize":329,"fontWeight":320,"fill":321},"111","122","generated",[314,726,728],{"x":722,"y":727,"textAnchor":318,"fontSize":344,"fill":321},"140","frame",[72,730],{"x1":731,"y1":732,"x2":733,"y2":602,"stroke":321,"strokeWidth":734,"markerEnd":735},"200","112","266","1.5","url(#ref-a)",[72,737],{"x1":731,"y1":738,"x2":733,"y2":739,"stroke":321,"strokeWidth":734,"markerEnd":735},"136","172",[306,741],{"x":742,"y":595,"width":743,"height":744,"rx":719,"fill":629,"stroke":330,"strokeWidth":331},"272","240","56",[314,746,750],{"x":747,"y":748,"textAnchor":318,"fontSize":749,"fontWeight":320,"fill":321},"392","74","11.5","optimised routine",[314,752,755],{"x":747,"y":753,"textAnchor":318,"fontSize":754,"fill":321},"92","10.5","vectorised groupby",[306,757],{"x":742,"y":758,"width":743,"height":744,"rx":719,"fill":643,"stroke":321,"strokeWidth":644},"146",[314,760,761],{"x":747,"y":717,"textAnchor":318,"fontSize":749,"fontWeight":320,"fill":321},"plain reference",[314,763,765],{"x":747,"y":764,"textAnchor":318,"fontSize":754,"fill":321},"188","a loop over groups",[72,767],{"x1":768,"y1":597,"x2":769,"y2":732,"stroke":321,"strokeWidth":734,"markerEnd":735},"516","580",[72,771],{"x1":768,"y1":772,"x2":769,"y2":727,"stroke":321,"strokeWidth":734,"markerEnd":735},"174",[306,774],{"x":775,"y":716,"width":764,"height":718,"rx":719,"fill":598,"stroke":385,"strokeWidth":331},"586",[314,777,780],{"x":778,"y":779,"textAnchor":318,"fontSize":329,"fontWeight":320,"fill":321},"680","120","compare",[314,782,783],{"x":778,"y":727,"textAnchor":318,"fontSize":754,"fill":404},"shrink any mismatch",[407,785,786],{},"The reference only has to be obviously right on small inputs — exactly the size Hypothesis generates and shrinks to.",[26,788,790],{"id":789},"keeping-generation-affordable","Keeping generation affordable",[10,792,793],{},"Array and frame strategies are the most expensive in Hypothesis, and a suite that adopts them carelessly can add minutes to every run. Three habits keep the cost proportionate to the value.",[10,795,796,797,800],{},"Bound sizes aggressively. Bugs in numerical code almost always reproduce on tiny inputs — the shrinker proves this every time it reduces a failure to a 2×1 array — so ",[14,798,799],{},"max_side=20"," or a thirty-row frame finds essentially everything a thousand-row one would, at a fraction of the cost. Large-input behaviour such as performance or memory belongs in a benchmark, not a property test. Keeping the two concerns separate keeps both fast.",[10,802,803],{},"Generate only the columns the property reads. A frame with twelve columns where the property touches two spends most of its time building data nobody checks. Declaring the two, and filling the rest with constants in the function under test's fixture if it requires them, keeps each example cheap.",[10,805,806,807,48],{},"Use profiles to scale the budget. A development profile with fifty examples keeps the edit loop fast; the CI profile runs a few hundred; a nightly profile runs thousands with larger shapes. Nothing about the tests changes between them — not the strategies, not the assertions — only the search budget, which is the approach set out in ",[44,808,810],{"href":809},"\u002Fproperty-based-fuzz-testing-strategies\u002Fhypothesis-integration-with-pytest-and-frameworks\u002F","Hypothesis integration with pytest and frameworks",[26,812,814],{"id":813},"frequently-asked-questions","Frequently Asked Questions",[10,816,817,820,821,17,824,827],{},[443,818,819],{},"Should generated floats include NaN and infinity?","\nDecide per property. A property about shape or dtype holds with NaN present and should include it; a property about sums, means or sorting usually needs finite values and should exclude them explicitly with ",[14,822,823],{},"allow_nan=False",[14,825,826],{},"allow_infinity=False",". Test the non-finite behaviour in its own property rather than weakening every assertion.",[10,829,830,833,834,836],{},[443,831,832],{},"Why is DataFrame generation slow?","\nBecause every example builds a full frame through pandas, and shrinking rebuilds many more. Keep row counts small with ",[14,835,469],{},", restrict columns to what the property needs, and prefer generating arrays and constructing the frame yourself when the schema is simple.",[10,838,839,842,843,846,847,850],{},[443,840,841],{},"How do I generate a DataFrame that matches a real schema?","\nDeclare each column with ",[14,844,845],{},"hypothesis.extra.pandas.column",", giving its dtype and an element strategy that respects the domain — non-negative amounts, a fixed set of currency codes — and optionally ",[14,848,849],{},"unique=True",". The resulting frames are valid by construction.",[26,852,854],{"id":853},"related","Related",[31,856,857,863,869,876],{},[34,858,859,862],{},[44,860,861],{"href":46},"Designing Strategies for Domain Data"," — constraining generation at the source.",[34,864,865,868],{},[44,866,867],{"href":54},"Testing Numeric Code with Floats and NaN Edge Cases"," — the float behaviour these strategies expose.",[34,870,871,875],{},[44,872,874],{"href":873},"\u002Fproperty-based-fuzz-testing-strategies\u002Fadvanced-property-based-testing\u002Fwriting-metamorphic-properties\u002F","Writing Metamorphic Properties"," — properties for numerical code without an oracle.",[34,877,878,882],{},[44,879,881],{"href":880},"\u002Fproperty-based-fuzz-testing-strategies\u002Fhypothesis-framework-fundamentals\u002Freducing-hypothesis-test-execution-time\u002F","Reducing Hypothesis Test Execution Time"," — keeping frame generation affordable.",[10,884,885,886],{},"← Back to ",[44,887,861],{"href":46},[889,890,891],"style",{},"html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}",{"title":66,"searchDepth":79,"depth":79,"links":893},[894,895,896,897,898,899,900,901,902],{"id":28,"depth":79,"text":29},{"id":58,"depth":79,"text":59},{"id":412,"depth":79,"text":413},{"id":436,"depth":79,"text":437},{"id":493,"depth":79,"text":494},{"id":659,"depth":79,"text":660},{"id":789,"depth":79,"text":790},{"id":813,"depth":79,"text":814},{"id":853,"depth":79,"text":854},"Property-test NumPy and pandas code with hypothesis.extra: array shapes and dtypes, element bounds, NaN policy, schema-valid DataFrames, and keeping generation fast.","md",{"slug":906,"type":907,"breadcrumb":908,"datePublished":909,"dateModified":909,"faq":910,"howto":917},"generating-dataframes-and-arrays-with-hypothesis-extras","article","Arrays & DataFrames","2026-09-18",[911,913,915],{"q":819,"a":912},"Decide per property. A property about shape or dtype holds with NaN present and should include it; a property about sums, means or sorting usually needs finite values and should exclude them explicitly with allow_nan=False and allow_infinity=False. Test the non-finite behaviour in its own property rather than weakening every assertion.",{"q":832,"a":914},"Because every example builds a full frame through pandas, and shrinking rebuilds many more. Keep row counts small with max_size, restrict columns to what the property needs, and prefer generating arrays and constructing the frame yourself when the schema is simple.",{"q":841,"a":916},"Declare each column with hypothesis.extra.pandas.column, giving its dtype and an element strategy that respects the domain — non-negative amounts, a fixed set of currency codes — and optionally unique=True. The resulting frames are valid by construction.",{"name":918,"description":919,"steps":920},"How to generate arrays and DataFrames for property tests","Declare shapes, dtypes and element strategies explicitly, choose a NaN policy per property, and keep sizes small so generation and shrinking stay fast.",[921,924,927,930,933],{"name":922,"text":923},"Choose the dtype and shape strategy","Use npst.arrays with a dtype and npst.array_shapes bounded by min_dims, max_dims and max_side.",{"name":925,"text":926},"Constrain the elements","Pass an element strategy with explicit bounds and a deliberate NaN and infinity policy.",{"name":928,"text":929},"Declare DataFrame columns","Use pdst.data_frames with a column per field, each with its dtype and element strategy.",{"name":931,"text":932},"Bound the size","Keep row counts and array sides small so examples and shrinking are fast.",{"name":934,"text":935},"State the property precisely","Assert invariants such as shape preservation, idempotence or equivalence to a reference implementation.","\u002Fproperty-based-fuzz-testing-strategies\u002Fdesigning-strategies-for-domain-data\u002Fgenerating-dataframes-and-arrays-with-hypothesis-extras",{"title":5,"description":903},"property-based-fuzz-testing-strategies\u002Fdesigning-strategies-for-domain-data\u002Fgenerating-dataframes-and-arrays-with-hypothesis-extras\u002Findex","FBuxAJ6PHJsyy1DGvN5EHprjkFu_73YHSsAtug2a-ZA",1789718769060]