From d4f8e76605d3d9c72fa99740aee6de164d2404a0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alexander=20Gr=C3=A9us?= Date: Fri, 28 Apr 2023 14:48:54 +0200 Subject: [PATCH 1/5] feat: Added `Table.plot_histograms` to plot a histpogram for each column in the table Co-authored-by: sibre28 <86068340+sibre28@users.noreply.github.com> --- src/safeds/data/tabular/containers/_column.py | 2 +- src/safeds/data/tabular/containers/_table.py | 27 +++++++++++++++++++ 2 files changed, 28 insertions(+), 1 deletion(-) diff --git a/src/safeds/data/tabular/containers/_column.py b/src/safeds/data/tabular/containers/_column.py index 1feaaa162..cc61c0150 100644 --- a/src/safeds/data/tabular/containers/_column.py +++ b/src/safeds/data/tabular/containers/_column.py @@ -589,7 +589,7 @@ def plot_histogram(self) -> Image: """ fig = plt.figure() ax = sns.histplot(data=self._data) - ax.set_xticks(ax.get_xticks()) + ax.set_xticks(ax.get_xticks()[1:-1]) ax.set(xlabel=self.name) ax.set_xticklabels( ax.get_xticklabels(), diff --git a/src/safeds/data/tabular/containers/_table.py b/src/safeds/data/tabular/containers/_table.py index b7a77ed39..91d038f43 100644 --- a/src/safeds/data/tabular/containers/_table.py +++ b/src/safeds/data/tabular/containers/_table.py @@ -1185,6 +1185,33 @@ def plot_scatterplot(self, x_column_name: str, y_column_name: str) -> Image: buffer.seek(0) return Image(buffer, format_=ImageFormat.PNG) + def plot_histograms(self) -> Image: + """ + Plot a histogram for every column. + + Returns + ------- + plot: Image + The plot as an image. + """ + fig = plt.figure() + data = pd.melt(self._data, value_vars=self.column_names) + grid = sns.FacetGrid(data=data, col="variable", sharey=False, sharex=False) + grid.map(sns.histplot, "value") + grid.set_xlabels("") + grid.set_ylabels("") + grid.set_titles("{col_name}") + for axes in grid.axes.flat: + axes.set_xticks(axes.get_xticks()[1:-1]) + axes.set_xticklabels(axes.get_xticklabels(), rotation=45, horizontalalignment="right") + grid.tight_layout() + + buffer = io.BytesIO() + fig.savefig(buffer, format="png") + plt.close() + buffer.seek(0) + return Image(buffer, ImageFormat.PNG) + # ------------------------------------------------------------------------------------------------------------------ # Conversion # ------------------------------------------------------------------------------------------------------------------ From d944dbff2ae67513a26a8b3545aae1a047de5ac0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alexander=20Gr=C3=A9us?= Date: Fri, 28 Apr 2023 16:30:50 +0200 Subject: [PATCH 2/5] refactor: Changed xticks back to before as the changes would remove the first and last labels for categorical data. This however brings back the bug that with numerical data the range of the x values will be larger than needed Co-authored-by: sibre28 <86068340+sibre28@users.noreply.github.com> --- src/safeds/data/tabular/containers/_column.py | 2 +- src/safeds/data/tabular/containers/_table.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/safeds/data/tabular/containers/_column.py b/src/safeds/data/tabular/containers/_column.py index cc61c0150..1feaaa162 100644 --- a/src/safeds/data/tabular/containers/_column.py +++ b/src/safeds/data/tabular/containers/_column.py @@ -589,7 +589,7 @@ def plot_histogram(self) -> Image: """ fig = plt.figure() ax = sns.histplot(data=self._data) - ax.set_xticks(ax.get_xticks()[1:-1]) + ax.set_xticks(ax.get_xticks()) ax.set(xlabel=self.name) ax.set_xticklabels( ax.get_xticklabels(), diff --git a/src/safeds/data/tabular/containers/_table.py b/src/safeds/data/tabular/containers/_table.py index 91d038f43..43bdbbbbe 100644 --- a/src/safeds/data/tabular/containers/_table.py +++ b/src/safeds/data/tabular/containers/_table.py @@ -1202,7 +1202,7 @@ def plot_histograms(self) -> Image: grid.set_ylabels("") grid.set_titles("{col_name}") for axes in grid.axes.flat: - axes.set_xticks(axes.get_xticks()[1:-1]) + axes.set_xticks(axes.get_xticks()) axes.set_xticklabels(axes.get_xticklabels(), rotation=45, horizontalalignment="right") grid.tight_layout() From e892cdc0a3ff7ae587c1ac6c5bb6c879ae6d9ffb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alexander=20Gr=C3=A9us?= Date: Mon, 8 May 2023 21:23:23 +0200 Subject: [PATCH 3/5] test: Added test for `Table.plot_histograms` docs: Added `Table.plot_histograms` to "Data Visualization" Tutorial --- docs/tutorials/data_visualization.ipynb | 20 ++++++++++++++++++ src/safeds/data/tabular/containers/_table.py | 2 +- tests/resources/image/snapshot_histograms.png | Bin 0 -> 12791 bytes .../containers/_table/test_plot_histograms.py | 11 ++++++++++ 4 files changed, 32 insertions(+), 1 deletion(-) create mode 100644 tests/resources/image/snapshot_histograms.png create mode 100644 tests/safeds/data/tabular/containers/_table/test_plot_histograms.py diff --git a/docs/tutorials/data_visualization.ipynb b/docs/tutorials/data_visualization.ipynb index 739dd1d05..ca1c3ed5f 100644 --- a/docs/tutorials/data_visualization.ipynb +++ b/docs/tutorials/data_visualization.ipynb @@ -210,6 +210,26 @@ } } }, + { + "cell_type": "markdown", + "source": [ + "## Histogram of all columns" + ], + "metadata": { + "collapsed": false + } + }, + { + "cell_type": "code", + "execution_count": null, + "outputs": [], + "source": [ + "titanic_numerical.plot_histograms()" + ], + "metadata": { + "collapsed": false + } + }, { "cell_type": "markdown", "source": [ diff --git a/src/safeds/data/tabular/containers/_table.py b/src/safeds/data/tabular/containers/_table.py index f4a2e01d6..dca580222 100644 --- a/src/safeds/data/tabular/containers/_table.py +++ b/src/safeds/data/tabular/containers/_table.py @@ -1322,7 +1322,6 @@ def plot_histograms(self) -> Image: plot: Image The plot as an image. """ - fig = plt.figure() data = pd.melt(self._data, value_vars=self.column_names) grid = sns.FacetGrid(data=data, col="variable", sharey=False, sharex=False) grid.map(sns.histplot, "value") @@ -1333,6 +1332,7 @@ def plot_histograms(self) -> Image: axes.set_xticks(axes.get_xticks()) axes.set_xticklabels(axes.get_xticklabels(), rotation=45, horizontalalignment="right") grid.tight_layout() + fig = grid.fig buffer = io.BytesIO() fig.savefig(buffer, format="png") diff --git a/tests/resources/image/snapshot_histograms.png b/tests/resources/image/snapshot_histograms.png new file mode 100644 index 0000000000000000000000000000000000000000..9ba2221b5ee64af7859606518346576f418a3037 GIT binary patch literal 12791 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 Yd^N}L-1Hkv+K@=(i;5T0&Rx6zPaKjo7XSbN literal 0 HcmV?d00001 diff --git a/tests/safeds/data/tabular/containers/_table/test_plot_histograms.py b/tests/safeds/data/tabular/containers/_table/test_plot_histograms.py new file mode 100644 index 000000000..f25d2f5a4 --- /dev/null +++ b/tests/safeds/data/tabular/containers/_table/test_plot_histograms.py @@ -0,0 +1,11 @@ +from safeds.data.image.containers import Image +from safeds.data.tabular.containers import Table + +from tests.helpers import resolve_resource_path + + +def test_should_match_snapshot() -> None: + table = Table({"A": [1, 2, 3], "B": ["A", "A", "Apple"]}) + current = table.plot_histograms() + snapshot = Image.from_png_file(resolve_resource_path("./image/snapshot_histograms.png")) + assert snapshot._image.tobytes() == current._image.tobytes() From f73554b600df8de47f29dc3ae5c3981079eb0724 Mon Sep 17 00:00:00 2001 From: Lars Reimann Date: Mon, 8 May 2023 22:00:44 +0200 Subject: [PATCH 4/5] test: fix pytest hanging when run from PyCharm --- .../data/tabular/containers/_column/test_plot_boxplot.py | 5 ++++- .../data/tabular/containers/_column/test_plot_histogram.py | 5 ++++- .../containers/_table/test_plot_correlation_heatmap.py | 7 +++++-- .../data/tabular/containers/_table/test_plot_histograms.py | 5 ++++- .../data/tabular/containers/_table/test_plot_lineplot.py | 5 ++++- .../tabular/containers/_table/test_plot_scatterplot.py | 5 ++++- 6 files changed, 25 insertions(+), 7 deletions(-) diff --git a/tests/safeds/data/tabular/containers/_column/test_plot_boxplot.py b/tests/safeds/data/tabular/containers/_column/test_plot_boxplot.py index 8d9212f59..84c0afe87 100644 --- a/tests/safeds/data/tabular/containers/_column/test_plot_boxplot.py +++ b/tests/safeds/data/tabular/containers/_column/test_plot_boxplot.py @@ -11,7 +11,10 @@ def test_should_match_snapshot() -> None: table.get_column("A").plot_boxplot() current = table.get_column("A").plot_boxplot() snapshot = Image.from_png_file(resolve_resource_path("./image/snapshot_boxplot.png")) - assert snapshot._image.tobytes() == current._image.tobytes() + + # Inlining the expression into the assert causes pytest to hang if the assertion fails when run from PyCharm. + assertion = snapshot._image.tobytes() == current._image.tobytes() + assert assertion def test_should_raise_if_column_contains_non_numerical_values() -> None: diff --git a/tests/safeds/data/tabular/containers/_column/test_plot_histogram.py b/tests/safeds/data/tabular/containers/_column/test_plot_histogram.py index 0ba8e1c33..fe4157b6a 100644 --- a/tests/safeds/data/tabular/containers/_column/test_plot_histogram.py +++ b/tests/safeds/data/tabular/containers/_column/test_plot_histogram.py @@ -8,7 +8,10 @@ def test_should_match_snapshot_numeric() -> None: table = Table({"A": [1, 2, 3]}) current = table.get_column("A").plot_histogram() snapshot = Image.from_png_file(resolve_resource_path("./image/snapshot_histogram_numeric.png")) - assert snapshot._image.tobytes() == current._image.tobytes() + + # Inlining the expression into the assert causes pytest to hang if the assertion fails when run from PyCharm. + assertion = snapshot._image.tobytes() == current._image.tobytes() + assert assertion def test_should_match_snapshot_str() -> None: diff --git a/tests/safeds/data/tabular/containers/_table/test_plot_correlation_heatmap.py b/tests/safeds/data/tabular/containers/_table/test_plot_correlation_heatmap.py index 6fa0e1da2..5ba397c68 100644 --- a/tests/safeds/data/tabular/containers/_table/test_plot_correlation_heatmap.py +++ b/tests/safeds/data/tabular/containers/_table/test_plot_correlation_heatmap.py @@ -7,5 +7,8 @@ def test_should_match_snapshot() -> None: table = Table({"A": [1, 2, 3.5], "B": [0.2, 4, 77]}) current = table.plot_correlation_heatmap() - legacy = Image.from_png_file(resolve_resource_path("./image/snapshot_heatmap.png")) - assert legacy._image.tobytes() == current._image.tobytes() + snapshot = Image.from_png_file(resolve_resource_path("./image/snapshot_heatmap.png")) + + # Inlining the expression into the assert causes pytest to hang if the assertion fails when run from PyCharm. + assertion = snapshot._image.tobytes() == current._image.tobytes() + assert assertion diff --git a/tests/safeds/data/tabular/containers/_table/test_plot_histograms.py b/tests/safeds/data/tabular/containers/_table/test_plot_histograms.py index f25d2f5a4..2e6f79ca6 100644 --- a/tests/safeds/data/tabular/containers/_table/test_plot_histograms.py +++ b/tests/safeds/data/tabular/containers/_table/test_plot_histograms.py @@ -8,4 +8,7 @@ def test_should_match_snapshot() -> None: table = Table({"A": [1, 2, 3], "B": ["A", "A", "Apple"]}) current = table.plot_histograms() snapshot = Image.from_png_file(resolve_resource_path("./image/snapshot_histograms.png")) - assert snapshot._image.tobytes() == current._image.tobytes() + + # Inlining the expression into the assert causes pytest to hang if the assertion fails when run from PyCharm. + assertion = snapshot._image.tobytes() == current._image.tobytes() + assert assertion diff --git a/tests/safeds/data/tabular/containers/_table/test_plot_lineplot.py b/tests/safeds/data/tabular/containers/_table/test_plot_lineplot.py index a370c3146..480d5b147 100644 --- a/tests/safeds/data/tabular/containers/_table/test_plot_lineplot.py +++ b/tests/safeds/data/tabular/containers/_table/test_plot_lineplot.py @@ -10,7 +10,10 @@ def test_should_match_snapshot() -> None: table = Table({"A": [1, 2, 3], "B": [2, 4, 7]}) current = table.plot_lineplot("A", "B") snapshot = Image.from_png_file(resolve_resource_path("./image/snapshot_lineplot.png")) - assert snapshot._image.tobytes() == current._image.tobytes() + + # Inlining the expression into the assert causes pytest to hang if the assertion fails when run from PyCharm. + assertion = snapshot._image.tobytes() == current._image.tobytes() + assert assertion @pytest.mark.parametrize( diff --git a/tests/safeds/data/tabular/containers/_table/test_plot_scatterplot.py b/tests/safeds/data/tabular/containers/_table/test_plot_scatterplot.py index 0ff0ed090..f23353517 100644 --- a/tests/safeds/data/tabular/containers/_table/test_plot_scatterplot.py +++ b/tests/safeds/data/tabular/containers/_table/test_plot_scatterplot.py @@ -10,7 +10,10 @@ def test_should_match_snapshot() -> None: table = Table({"A": [1, 2, 3], "B": [2, 4, 7]}) current = table.plot_scatterplot("A", "B") snapshot = Image.from_png_file(resolve_resource_path("./image/snapshot_scatterplot.png")) - assert snapshot._image.tobytes() == current._image.tobytes() + + # Inlining the expression into the assert causes pytest to hang if the assertion fails when run from PyCharm. + assertion = snapshot._image.tobytes() == current._image.tobytes() + assert assertion @pytest.mark.parametrize( From cde920aee1a7ac257b0c616aeda752477b9319f1 Mon Sep 17 00:00:00 2001 From: Lars Reimann Date: Mon, 8 May 2023 22:13:07 +0200 Subject: [PATCH 5/5] feat: wrap diagrams if more than 3 --- src/safeds/data/tabular/containers/_table.py | 4 +++- tests/resources/image/snapshot_histograms.png | Bin 12791 -> 0 bytes .../image/snapshot_histograms/four_columns.png | Bin 0 -> 24457 bytes .../image/snapshot_histograms/one_column.png | Bin 0 -> 8359 bytes .../containers/_table/test_plot_histograms.py | 16 +++++++++++++--- 5 files changed, 16 insertions(+), 4 deletions(-) delete mode 100644 tests/resources/image/snapshot_histograms.png create mode 100644 tests/resources/image/snapshot_histograms/four_columns.png create mode 100644 tests/resources/image/snapshot_histograms/one_column.png diff --git a/src/safeds/data/tabular/containers/_table.py b/src/safeds/data/tabular/containers/_table.py index dca580222..747b1b32c 100644 --- a/src/safeds/data/tabular/containers/_table.py +++ b/src/safeds/data/tabular/containers/_table.py @@ -1322,8 +1322,10 @@ def plot_histograms(self) -> Image: plot: Image The plot as an image. """ + col_wrap = min(self.number_of_columns, 3) + data = pd.melt(self._data, value_vars=self.column_names) - grid = sns.FacetGrid(data=data, col="variable", sharey=False, sharex=False) + grid = sns.FacetGrid(data=data, col="variable", col_wrap=col_wrap, sharex=False, sharey=False) grid.map(sns.histplot, "value") grid.set_xlabels("") grid.set_ylabels("") diff --git a/tests/resources/image/snapshot_histograms.png b/tests/resources/image/snapshot_histograms.png deleted file mode 100644 index 9ba2221b5ee64af7859606518346576f418a3037..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 12791 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 Yd^N}L-1Hkv+K@=(i;5T0&Rx6zPaKjo7XSbN diff --git a/tests/resources/image/snapshot_histograms/four_columns.png b/tests/resources/image/snapshot_histograms/four_columns.png new file mode 100644 index 0000000000000000000000000000000000000000..45e050640b173dcfa99485b899d8690536a891ea GIT binary patch literal 24457 zcmd_S2UM2Vwl(}}5;a)R2r4RYRFEbr0)kY#(tB?z0t(WkccLPS2ukmu^o}6Xiw%?_ zN>ypnrAe<+{<%p`PR{q8bMLwB8{;20V%4r+<1&e(MKWw(0^` zn`;uBRMiIx?61~UU2Be!mtW8Q*5qkVs(Ph~zFu5ja1V23Y?Y+?*4GkjhKWI|#P_qZ zvpTxC%<5iWy+QBGhOnCNQz0tV!4~=q2DeA5xcjPXc9-qghcBnN4$rF6lV2zoHd5^& zKR?pAZ6-gjd7NBFejYuu=ST80^zr7^_~9xgw{8`Fmd3H-9`H5=nv>*b2HSu5?W5{< z@7^7sno?>nc1rbHO*v@OY8AM9ll!1zj)~UPw^ohNqsEy6J1My_@$rMdu4Q=79H_ZB zzAo|0mkU#aO)@|K{Il}8n}Lruzc4DAYSgg*^6T2Ch7IhC^MmQi<*O)xZAA_ooSafu zuU-{De_l4*u)%V1s!4->J%vfEz-C0b+>3syCs1d=ZM*jS-s;e7$GPiM0}T@U&Url* zU?}u_xkEQ))2f9}5BHs;p`nSf8f;`ccu;z{Ij3oMtSi;)CrbWcT7l))_j@?ipT5by zhPUQ-dT=~-S$5X4{BWkx?FWijla@{RQRB}settCJHf?q$f%F2F=eKK`{mT3Ni~q+{ z>(VDX+!>SfDgz8ZJa$`Jnkg}AOi}C1T2E>F`sU%j5N-pn5MHCWe5*lq`n9gr^g_0> zm4Szv@K^WzPKf!PppmN{pd2@EvmfMKTpV!;v>bS!l9F=333oa0DJwqvIpuh*53_{j z(p;Z}&-Lrq$!#!81iUGCuTfj2YSi~G{76{>zFqm;3FhY<+Y6<|#XTC+w31KUTqEX_ zq?8=D>W4L7Q*+uf`ROQOwaF@nm|0jjjvZ4oYD!a5RP>vl?2i_9m{z6V=vr-ln)cl` zhjkrR(uUG}Ema-e;ADQETS)dwWkQflq`!sGR;L-O90%=Ly+ty}kg zmucan_( zi_jGAbla@0_tuAbesQLq*KyV?+qk)L*s_u{$Lu33CH+BYX!7KwWo&%B(O_dL*+`b% zZ?<5M)eKyrMA32UvxMoxOpJNC{6C<$s5{EQ%cEQ&nrg-e?MbG|FJWu7|VI9Y=@qK!9AU7 z%9r~)qA9k5Wo#6A=7YD~sTq}zA9vlvC~^%CjP(W=m1~Y}*y_J1b^dnTS$F-5=h!Z0 zbDDa#_~QJO!pqwmd(8NsUit92q(#4{!hhiXqnlyU@nrXY$}(`+OGTmC!pNBZE?h9# zdiZmaPD%0n%t*SQpI=Ff$@3dO8Gg!)U07JCPty|Q)OeP%x^{KOt}<4N^7#2ZRd3(Q zUAdB=Ulk;mG-WgVIkwp8dy-1Jwz#x(V}e|?WsUI6*NGa@nbW%|W)o9QI!4`Z9?pL& z7HB~sj`N<}2hg58R@fPel-km&G9^9s8OvC0(a9Lg&XfG*wcI}h=uWwg+(xQZB-@|hZA8KPITGyoh`omh>)+zO3N4rE57C}LcRjXE2 zR8;tsl(?wr{*dwThqap$oEPWk5q+$B{8hFUymMXkgP*T&G>%+u0K$jV#fuk1j#>2T zw5`#lakWs|qU-mH|EDj0hRQ8hE?s)Oci+C&0J3%K6Xcjpe6b^2S~Q#HhK7d5J^Hv3 z6B8?{s-6nmF7N5>t?0K@r+?X%G*FU^&12Y-`=o)ZP+VC#fGJTrT!1;vHz+6}C}>BH zo}8QXN;i0OJ@5d_nOR@7E%#reQM&9Nol88)aU@oD zPO+ycD@mwPLw;S&nQPZlnprM>`t*rv)23&KKLqRP>koW<5sV>*8flFw=pm7dU|HAzJIh#kdcfGtC5mQN?(Q+JnZJ?PGW1fV7`8sXn zE}ruJ?UZ-Yc3yJwJx+(*D%_j0Dqj1v@A15IM;3>=Ig^${nRV~p7ashSBz|4b$I;PK zW95v4m4`|c&!2yzWAc*!r>Mt=&#ubwIY-rjyyF)ZmeSpQSuEaJc`xEF*1$)a{_)|< zZ#Fm0BPd2XFU=X;-^C~0{LHwHBfao3wdjIu^f~Woau~R;cb~5g=00c9_fBc}bGCm( zL>eL-Vpmo6y9awt(d^naD6stg6!q1)@t%+)dLCG>IF<<8lZb2zmX=Rxmw%)v?I4GO z9CM8v>5h&LDr)LLrq{1tu?h)kZe@}7V~UN9&8v9Cdt#Z}*g=q;x+V**rb;Nx@+L*h>VH?FH4hH|`QLpP-nXjh)MD@aIt3 zre@u2oYViT*@VOWG-Y%x2_2c;zeP^U;C~xh`7eT<|CetcSKSlFrF%Q%s4=^!sHoNO zXC>qw;ja0*co~&@+c*`o^re1UyVhv(>jxyM-1ts%ZIq=O*mVnY6SP~mM(5`8lgxw_ z!7|n^Eq=eJp<5M+q1^^6d@oHY@1zVX{1Q1gE$OqIXD)3%&7m(W*cCriTwN(m zZ2bJWtGE7u_Z)9f=oP*N?B|=Fo{k{W5@2xoasr75CKIz=-h=N1N9u)VTK0(9l z4_S^B%?vy#XYlow`4F*l*REuQX%geFiLXF!zZ1bTn~~Q};hZWWVi>8NIPvQ*zx)E6 zAvbjNoHqlf{Jf)+6SG)Z(4`!6f$;H}85_ZsXa7zM?-ZApl=Qs4i9QKgAWhnwT?9n3!uC{V{; zBh+=5pFB@j`O))x{9?X-y>jN=$ z%zOqQKTQGOU&*$IQUEw}f!3QIAKR8B6DcxK7hj`hs#4o|_NDUoo$8<&tQyKa@D z9Z3S|+C`du>q|~?TYix%lCAY$s7!x* zI|IipZv84*gsPstzIZ%YLu66|&1JyAoyV@n+_-V0+fu;llV0G}PnmiV9wZ=8X4f|e znKMuH)u`Ys>c0z1ByU5hgg7dX`U+cI=kj7Ax{*jS7jlZ8;_6dd; zRaKI>X^z8(-y7Qj93q-34;>}3w|HS(qSd^dUj5m%VC!!!iQh*`bXcUrWHmK4pEP&( z_OkQwHIZFikn({t3~^SP2GdK@2!t$kU1dM?q#XBD^-IoOZ*LV8B!e`& zcWeH7m%JxxfX!l0ib{F}Vx9lPhaJy^_U${haVHO}btp$j|EZ*L!7;XL?l7>-@$*}wm+Y2n!IHoHE0`e_!9O5G%_!5BA`QS*0!mwS)+anZP8c>K-2 zh&wAAjP)mPnv-H4US7(UmX=QfQ#JE4-(=pov(5>KFe2ZM+5=H&o6@|)|_f0?8q zAM@vvt;zE~sHR*M?p`Gtv^%42Mlf=dVy{)WnHDX!y<@iL54{dMv6gEe8t6!z@d zL$E4SA~1z|k$rZYRA^j6Lc+Mm&!uIEx~gu%weOwY_WubH^h9iJ(?t+eK3A%SvlX$E&*Ykw zr-gpJIXf?}QgCpva`|X(#nSH64JS~SQ{VCSZi*4}P4W7b%KE?{pV%fs$K(i5y_2sF z$$pBHqfxXc-Lp&4&@c(7CRVrHOP&68*U;?lTQ=a-N+Wsx&oU(bn-2i`AK(C3Ezn3> zb>!8Dmp+~^)`&j7cTydcP^@40GmYg?UNKyd|8#Y(R#_rWErAYNBVN8PvI@yvwvtM= zn;s8Dk#9MD`t%jVDU(X&6xBy}?%er>AP7`DJ@gy32T!Ed;?Qq{IZ2y#YY}H4~-(?kB+uRo~UowkvadDxXXnJ*bE3+8M-F#X;G{5}vI94Li z@U;gm)y9nteryVpy`iQ@`~_XFujKvRN2sDI20?6jaxrU*I7|;+%K4OW@zk-n)vlt4 zrB`K8IsA9=l7GMDy=h-y*Ct%8iyNPqP-sfi$_9raZ_H=fCUk7~rm2zkc7>I*Mv49M z@FYK~8&V>HDg=B~0d?{C@nevJOWYA26TXqvmRTq55=dG~K4=2yn}Myqmq zn=7?3V)@-Z5<**6LV($}3e9yBwPh#z8$4dz- zL15g9ckitK0Ez1_>g$vHpxv?K>ocLlhh_Ww`(K-6*-sg+P^ew^`NW+B$HvCM&6$PY zxOubEZx^}Hp|lqUbi$6a8Q%&gq>IMNb`cIHG3Va>`%0i42A|rDv|X3{nY>a-Cp%O7 z1SqZU!NFut&kZ>}D6k32e>mj=05sfIqyd;_!O6kO%1;}o&KnyO^R-su5NBq1UB6#l z{{bUS@+qau9LPaCc?}bASis(zk=K(N8i~szr9D$m!nmza4O=m-ZX;hy6j%_pW5=i^DX-yBZhFWu$%)^<;%2@?#OAxdU*ghLOVS(*2oz? zgxo8}p1GTRyR5U2{JOOpc0_^GxzhRSt~dx1Rn;I+dEY{=>_-F;5ushOISb7HMZ^XWF1`uC0WgSnamim9r{RR0pTntG2i`Qj2iFr zwIwh*&pz7QxjKW6d>sO7!uSzxZh~vN>n1>Lp@`UCAcgXm$Efju)A(B!La-z4*B3e1 zERI(mdYu;eH|T%%jHlc$0NtIS52AK#*|KHlk;`{pym(O`eU6$_Ggo%ImgPfjhvDo? zO(38DfG^MQuKM`)lh+^bRJ8jCrD1Ho7ZM)_YA->XZwIEwKYmoz)6>h7c9{9bX<(wjIv!Rbg)nBpXi=rW@5?>prxT<-LvNet_GihT9=%G6DLl**8XNG&@(bJV!hh6 zHsKDJIP#}#;Y$AW*o^)KfgoW)U)wvH3cqF9W^EmB3F(hiV=36NM zUJ^~CX!ON;f=IvR^)YZP(@~OG|5!pI3Y7l(LkPe5V}LK(ZQJ5{Dgz7lo1aHg@UCH8 zE42WQdnebWTlib64!7+|B;5g{V!e$UL{}ZBE!**15%+8hJ$>reuU{`6(#X3W+dPK= zd-e0vtF#ZODAE@_S6}$a+sWMwJU+p-nkx{5-pPSjz!kow5yKF?jm5>^#-?(4LY>pkMYk>_=sru=`|x|MeK z?i8c+B4xn82~bt|=QtaVGpc8$eP=!2XIy+)pKoPU4d4Oss5V6{%R_4!`au)^P0z?U zoDJ^Sw0JJg=qTsPdtb9!?5V1(RFIeV0oY#7m1kr;bM`Dd7uOTTCiBU!a)ghz8mMP8 z`u(f??c{@hgV_c8&)I2q>_`9>_a6{ou%oQGGw z#%KG&;YGluc*rIFV5$SK!|x8FuIKp6UvPRhZAvTkBQKPl^2^)Yn5-gGTwGl7=8c&~ z#KVV=)@20U5P6jB?eoF3PSiBb^+k@{Jx?yfwS;8V3#Al#ezW6X z9la+hxWw7wpRc5ZGi5r0U^z4*tJ<4*{?!A+t8*qty@m*Pv8F45q5 z19EWi@F+rY_*~-R@?u7uN@4Coz=Z?B-4s*7Ya`+B}h6 z{FD5rsD8^4=|~VcJ-`0?D<8P$AK`gccCbc%FWy_0 znCU5}WL21PGfBW&(W>vrQRV61)R34YL6L1DYkhruG?&Yg{sKx%zQL)maB;Cd&r1P? zEG@H$h_>&3DfUbue*TX)cwz&2`i>><^Sf8QK2C%Fk)~pUi;6dcaEjMx;Pgm)I_S7J z13UVvw(2S>(H|O$O7fytYW?JSUrkL7q~W1*o}To~;y|CIZ?m@GqtsVEpuDoVT_$|c zArVi~ern)tQ7GwKf8*dAO+M*9RdyS0C5cEB^H3 zkA59GvLcyTStP0!In3A(ufLOHFgfk$GeiDc%SdMg{?Q~yZcA5vWO>4k8>@++4PA^g zr-;h`jGisSV^#(}8=#JZIfK1Lif;=S?Y<7rF^$yx_FvV;8Tr2}=%YYOO9)JzB`NX4 zhYzn!5T72QzDLHa$i9Xu!sE^z)-h}7kYUmW4|^cjY0s6?;O=Jze!r&QuFR(*n@8yt zzf#K%tLHy$x83XPFGm;Q&gxN~x-h0akZ$*V;(hakSX<!hW!`x|}tw zG4NbjjWR_qPu5)wT@`oBQwNeact2TLslUDCfRTthSDC7o_zH-nO|KG_Tw!Yy!v0M@ zVb&MRn!HcZzQx`DrV8zgc9-SWfpto2e9I#QBith%Mn;-i{piQ7{dLudUtpkt)h%&j z5QLv|Oe32|L^_8QvpHYIhhg#i`pHs#@N#eXZo9)q;;8V!2~V%qm4Vq0hS<|IaHVXA?$K)>zckdGur0%mF^!WBH!Pm*To0UJaIkRn zww)HPlkI2pjU2DnxP!%rNbU?fCLbItFRwE z>-u?QgvM@h_0~J(pzedo|>ME9y_LTr?>dK3bWnbi1zu z0J9qKxe83V*3HtHb{A6y$I%lgLg**J>8OVsVrG6lJ3k+SN7RTXg`-q8-cwoaNylBq z*Y2omJWipgs70iImF5Q%xog9H5+(BGIEYKI3d1ycPr+n9k{R~gJX zmtpa^AYl3R0tyM;wEO|~tZSc++^c((0)F-W%Z|fP9<4bYpke}&I!Qu&t_GJrpl+hX zCS*4r^D{LyAD;fx=5f%57B4sQ1;IiV3V!RI&ow9}e0cX!0C@BJ#-K2`jXyu-LV)51 zyw=CE^n$j1#-y{yw3ynPUQn6s+e??_@9n*xjnb_&j^eahG$^SAgQ&kjqCTytIF(b69XUCQ9HU|s}mE1*{coGDWx z>INV1M(2ems3? zmzST!?Wp;rW(rZ;fcginDe&4i0)_=3&=Q|Cc$X#OJ?vbDV$X0m25>4BAz&gl>=%Ow z=Z#`6niQJkF3#TH*<&B^_&OoN8Oj?u5?(a&521rVDAhKle2I};* z@z|&HuoGCeJI(aNIiZ3|E5fdq&IwhS{ntnCPjQfv_dVV~9*M-kywh;`H6jAUmn;sO zk`nY_!O#&c__lCo0pKiH5*JGDDTX0s!BfKy5fD78&k}!OAtKaHZhcSend~bc_KMNE zTzOR2UtcuSmW(n(9h#%uL~pec)P$UNXAK;fre@QURK7ObQ-D$t%yTgc>Dg_LjPrAI zS7%2%w4E5mYjH~a5zb$_Qx^mf@g#qF={vI+7C#JtiSxVH0`8aa=5W0bh8dp~9dJ-9 z3Bj))+ln}yCM!77Sy@=hv-pO_JnRC9-)-9g)e9$2o|HU&`j!~H6k5}`j9|pamKJ=& zw*5T%I0SL<_w^#bSZX+remOy+C{WTx)O-eV`3erX`(+}oA)Uf=^e_*uHOzLC&YASACoHP|Nk+-eqza^w?8YtsYwjTutNYGiQR4 z6B>-6-+Ryj%H$0{YnFsg%lwHbpA(25q1Z5?SeREWjuX{s<#uD8uj34AB6@KWco4I) znHYtPUD;bwH?RKG})onBU@nMLm`#y z()IjQV3Q8|ATh5$F0=}H`xTCU$~Hd;dMdl7|) zF3Jo&B}sfi5jWV7!;vmad0b_S?+f7ocnwEd^H6(9dy;K0g6~nF+LeDr7bo!)$m->N zxv{>=v}95FQSc@Pp|93U4JtZQN#D&NIrZu+&j$`JAe?ns%{=0AX^{c-bc6(rWvB!9 zhJHep$b^NJ3oKB|sAIeaGIBqVVxnC1ZIG4TZ&9*q9mRsx#fWY79=V!bxOu0=K+sp% z$%1M`zE@c}ns>OM4!N3EI4&;X-M5IqDhW?#HN@5`#I4qY8ZhLbbbF=-zNM9A zSd9f~Js2uzlXh&RaH0yrX3Eb7()8Pe>?g0(L9yPe_a(bYn;Tro5g^uWu{Yzj?0Wpm z%FD~)KzPr@!lFUHHva9qcSliX#bIlkq^pr^LxLFjQwSH9^gfgQ@sS}QeuD^JS)yn1 z)9#pQmRZ(rBsl_dXEi)0t&NMIV5H8UzXRq8855}e1rgO4s8P86+(6z0%k$# zGICxJ2zw<}wevDEe1MSLMDT7l8X!q!+ac|te3$h)Ghl?2>L6vStV&-8;+{`LL_{A! z-eg3SP+;k~)O;o_SIBljy>kg5{fl}r9G>ZU)c3x(U4kYcrK+m>BsR&UHD4cMFYT6L z&)%9y)3csCF2Yryp>puW_D z?`u#!UI<6x?T>5LgusQeOGihiE>VGHD?Poj=dr1LGvMA(g16wFE+6`wT}hCY(8B1; z{X9JNglFstNj6B430OI|OS^&{mly(xJ9ou1A5X?xjmrN-A1V#6efInJP&jmU>U!S2f1elLeU&^(MM_OG zLP?@VP8tC-bLFB-7Y~*!T(3iNvw%1Amz-b}uLyhQPU~bN#6y${0jYc8nYBrsAsK>P zBXVIVIM`s^R0wFLK!;)|>g4foUwrZ71OR*}c+%i|hl)RX?&MASIHjPVP<`v?t&+!& zA9tgt(y6WLL)l~La7b9cYKO$PmoL;_uV>*P8JC#IBo^lVXlE{pm25t2H+~uWcI*jK zWE`laUL*@%5~Hz6c>(hhGpNg`yu%7eLLm?4V9}&J5@-T911BFiOA;--U6fK!Qgy(0 zdEr}b*Cvap29?n7-wJ9DpuV$GGcUNl{h;O*veRG{Veh`HCGT}IzJxAP*x^|}gc1<% zTriYyONH=^d3e751o4y(qG=vzZH{iU4^TIPU_|ou_gCr^a9h3&17(og`t2bwR86G2 zELIM0-NG>u{?4PX0|`hUpvmx=X}k7i)Bb+%W#CE!^4Pm91%Ur;-SmVMiaW^x@F<%*k?&qaN=f{20J zg5fr>KvRLnlK*PIcj+Zdh62_@SIWHT2i2e9Wp&`pC*HC|(x|z>D4-9%Hx6Nu_Gs5^ zus&_cP-bO|KJ7BFN;L}iD&ij5bB+t8b^SsY3ec;4GQ|J+V=g-O?tFWj7uPm_k;d6?)~g7GEa79f!Bh-qEi+vQm?_1 zo9hd@4>A!#SKj>oG!8at3QwHwm`gf+@8q)(Xrt1+-JPVeMaW4aeuDt?w?GiLPI!g6vVw#k&5F;9Ixx0s= z&LU26nzxqoV>PtH*T!j+zV>w>)R1ECDy)Q{+zk6 z_QMfNlEiXatwW)z+*etc{8?xuP{)}IuRJi`YN#0Y!W^9(pES{_5(jabM6Ec)*)!r7 zLD_5So`E0T4*d<1aAo(nheS_;w$&%ktDDORy*dv^gy9np#T5p@>ZtT}?R5|?JTPaZ z33Iu5F^ZZHr%WB({M^9GRFxL_Sk=&rFK-6}T{IidJw9_!5-=Y*|KK=-9xMs8_N-Gt zaS&Q8Bq2aaLaUKNgGt`f{NTzafm~KiZ_{#7z~~ce%Co~p^HO*VRoz+yVr)Z z5}1Wli$AYl&kecAOk)`ZlsUbgxT5IEs}AZmo=dT1*rvZ5WylbD30#az0RFaYeC)Q8|Q07t& zTg}@W6;9M0S~jhE1fk~W!D*{M-u*TX&n1m6J>Y=arGv$@m$=~{_JxAAhj|Dd#$NCp zM@bi0Fzm}n=%lb_cBURg2DDk6bvSb@&=Ul}X}2B#gZ3(*O^wSh{6S)b5ZYx&m$ov2 zFyvz=KI(cay}4kXHpf10XZxje71<&o?hN*CvQt3vOnz%^hMre;?PJFCRK=p%&U zS|^`5nifqdyY>7qbbyz)p*~rqX85p>uC&xGsN05w;DDE-s?csiGd2U{N1Cvkvdfpd z7B=%nU(tDD3QyNp0a4@6JQCDDOi!!5zHeT#3?!fn^U%;dLoW!~YFw~U|BPXgQ}2YR zAj+ydcsi=!NH(^j(Ut6(b#p!G)@25qNQj8#xf&PEKqzBPj;O4AA|8bpxHP;Uo*|t! zL;(TfI0ckNTE}7(MCG0FA`VcW_qHEWhVyWKdN{dYq*wzm+uz$;#_sj?uCA_VED?dI zJ5inCOvS_8EAE4A8;`A**C-4|)W^r?DY2>SJ|>S0I5yx3Vm%4va9*FFdFL%u1Tlb^ zO0|iMkyB%1asc3{dRX5so-;$}v4=XVOi+NE(7U#X^wDX+^5F_rF>*#kmKQ9DhnSmN z0dVgFvh|9xiv9#Je+?+t4!F3uq*vPvrsgQ07b$&594fE_p##PVE1i#^Hq~NLa}@<6 z>~^TUAE0neA>=6jWFD1_6=?u~naC$)?%{RY+tB0(i)xv6G}VBj|2+|NLCc8w5T+QQLKUvUW{vmq#};`-4jqyq?NX>{h{T0TTpQjp zSYt8{=)lo#Rn6P<27Uw(KtA2`rZCn(m}DPvs?d2`QxgcrfY@FE=;2*R1f~ekpKoqo zibKTg2ato;r5g6hy&){3}cax@(LYku;IO&KqRh_TzRSKTHyClTXgk9>1S{Lz`3#XLZZ&GOe+lw;7_M)*)yt4}r9`G5L*}v`pXdoF|t_mZ|gT^R;CiW zvQ5WZ$sVH>2|!rQCW)mA5Bds3eMxXidF|7|`rb3Q)n3==TYm@hMv4mfZg&8@a9pc- zm{U{g@@&p|VeFhj^x2IQVA2C@o^mIplx#*>Z-A_&C$coFJ5jG76AZwbzv(%rDeaNU zuSj=Sq5aUosN4L=3m!94GZ^n@PbM{llH&UIQNmgbHRsd-MOI9Xw3mE5RN*s59lyLv z1p2^!cJ>OZB?1W*;v}C;-oKO6$AP2qZSSF8Bza!^JLU1Rpir3c!Z}jwpl^>0G1yY7 zR}RT%e0;nL*6K@8HcSaiV=nF&*lbcz%^xmJfD=LB0pExH^`~t)3}{80KMj|lNF=!e zkb*r%GE$<Q z#Mg7ZO zEW~(g2vn43AS8U=O-x+;h_L+KCesKXxrJGadvl(a1j z>MS3DCrf)q{a&Z>r_9%+7=r<;4*sOAn>Sw|AQvZ~U*Ja1q%fmf)f%tc?|mFfg&xwd`BZP*doY&8 zpI;%aK^h?XtT%%(NW-Qj)$|+#$NBzv!m0r&)8IF%tp?Rb4}dx8IR$gEqp zZdK?IUA+D*t6Zf%rL!-@6cEfr=j~XdCoS*9TwZtboAMx?b*zlEBEk<#N=kr1k^tuN zsvqRlHC^=M$4p{qvX%qNJs_>%m(%AP1INK2w61y3XEZ=dR$+U| zp;oC5 zxW9Ym<;k%zwi(a1r5zG3RmJTEHm2lsh47h1kfcYF5F)zC;I?SfQBY{MIPJI7#QVkn`lta*ZNqC6#`t%58=S-U}x4|G_Z(4Of~A7q@e zAW`QGdl=JAnpL?3pXJmrj35h)EqS&@)HEBkryrn`F<9KNQ#UYS4Fhk_Tq6;H1n@wqtH~_A6oLOPm*Z0EO-|2&;oteGm5rX*w%HBG1QN`+*Aw zu_U?4{S?~aPq-NrP1Z_Gk!E!9~EF2c6+=cX+>VDMCsU^u#M9&i{HS3H0OPh0F# ziR_9Dzh=XZzvz0dp=LOMejzBY{iKl}>LreNA=UTc(TadE%N0M*dRphwW&{Kn1H=)P zGvQrLLlNENt@wRz4yjrO&$PIQah=2Wp)mlLBbQf93pK?$YDdIR1x^Ps7S^K0iL?Es(iJd=Fi3pB#VDn!^UlrB6Ww2X zqUgG54T~L-;aL{PTF>FKiJkx#t2zvDU@Q&HGevD3&XWFXS2rq`m&V{LNXK{stZ!u3 zcnePlK@Nk}FC9r)3No$|#N8CH!To4QCfx@hXu+~|TWwuq?;!H_J2(E@U*G;7SIG;5 z0^0W-dT!lo3wua9y)Qg`A$*g}K>!oLYddn1(UD(v>|rq+yk-sD(f{%+J3vf%!d4VgZWiII`#?eFim4M!yn%k_B_&2>0e36S8)8QlpT$kFj*;zqpA*5kJk8TgfwU^`^8VioyU zhlKpyfC7AgtA)bV^&MpYZr&UPxVm&e~diYf%Hr zQpQPafFBJm^MxL3XxXxr0i2|n3kiZWZRy>|jzSaSdN_-)^|Q}D~XWEr%I~8QXCxY8ZHs;Rc}>HTtV) zs|^XDjZzje7z*A_btD@;()0t1TWo$f*gg2BSkWB4w6w(VcBKo|TE4nYCCJ~OS&Vpg z?+!BU-%t7kjnzPiY~xf{>^zDUtf;QG3aa1Z zH+XM*0d12+Js^QRVr=WQhoXm#jouizO(!QOSGuoA?}|`BgCRtlKYINAuK(WH?=O#7 zL9yL~4+B?Vh#Mx2H!SJOq(f!!nF<#|&-3Tc8A`%Ee9(c5Iwxtc)qI@V(9kOE`oq69 zsH_Bw()D&Jo6nv*XM{-{0Pg;%)H}J;nxjtq0FOv6XXTqWvXCCdTSyO9zok7>GT#`r zVUoao4OM#bkX<+W+Xcw|gU0#aonJY6ST|Ypxu|uKCqUZZ9M&OYCz5ataz+kpt5*9D z8`iCpMft`oMvOCsHaePioikrb%kS(jO=j)#q00y z9}CU_#p4^R5I7)>!cl*bUifz?gtSm4HIP0xoS4Nv(Pj9v?!DvRLJkb2G^?V}J%dM7 zH}`r^#okI{muN-nx?a(5U0cV{cSphj<#^BOe{Hk>$4BmvkN^LvPxaq)p8a!|>^~e+ z!o6YzKepRzZ#??J{??&Kb@87y77N3*ihpFONPDN#{G=RelXvz7Cyj&!5{Of9)q97d7zYGWA7sXo-Ldik&?GwbJoftGAMOA8+V=c+wW5Z*qzppbW~x zi-lq(8s!zo(WAsBUk6T!LyYv5Bi#@u8@;HrCh@?Nu2843*W3Tpr1%2GQX*VOX%k>K zePW=>8lo=-Qy&!22RAql3yUz6JYxPsK^qIOF<7#^nBHtfE|O9ac#i5Kyn%Y2Y-8g< z)CJ*;d?sPIkiNU2WM~#8Y+rh?;>vHbPyUlT+Xr${ti!jw zbHrA`(n%JMBK|0ZB`xN+zI>U)rpRL!ao9{e7#kuc59s+6jyQs+;Utf^vvR6t*9SPd zALincLywzDB{*2Wpr9*fR+=a>n7xPg2xw=iLm%amqy0j`D_sR`=!fX+%KpaZ zwUfzyN_KzO`0&Jr2{Mn446r4W=IHo ze|=lgzuS-b*A+lRgc-Vh(1ED}L5YbC?Hb@nNJ|eB8z*PA(k5m$CZ^Mtq6{l9cH=VU{i%?S3t8Y6^3{GK7pXKv-7F*qP^Sg+wAP@;@@XR4w>%) z)lSfgG_gdVrXf%8I2*DgBzL8M-1Y=OhMEZQH%$TL7t3Sv76Pu^4uc>U6B9$X^+K1p zlOqisUE-@(uYSoPEyJWS0E7Bk;m9TATgA?mYg=hu<^HWL?=rOxJ`QbS_gdqK*AK2d zLd=t?Md0ZNFc}EaTi`FIEa=RJcx1A&_e%4!E(d&o0!^AYk!gv!e%nFK1TudF>Yn=D z=2w62gnC6T^`CV@`3(pVE&!a&a7$j+E2~Soy1;fl0>dcqB=g8#F8zj1Tl6hFB8Bgs zJ%fD>*bw~lrA2Atl|LTEztDlo)DNv30}os4 zVUZvM0uL3A-aw*^M%XOhYEC}xx6a~!w+CvZWH}8!PFC~|Y}dAZ1W`T#Q7{2%_)l5+ z??(sJ4le*m5T-_^m4|%Om))vIes6}dUxDoZnYoVSgZ_6M%6~7N{vV_CDs*z2{+xAO7ncp9JMb zjyJzGHsXkARXRHU^{8LU5tEcugx=Vk3Gx}HrG!__Ff%J5?+!YWfplyb1hSz&71p0E zVztkO*#v?esycak_6Z6r`JjEhdwxcl7y1brk-Ai8cQ8P#X<2TBQH#_4rCeGLIf6DQ z%|UpgH*IA|-8dYfu>YifG$w&VOH^36=v6apfTW1^I6%E=lZbnL+rF#sER@f&+JIJ1 zZ`e|yrR1{P0S5hVIZu8c&)MTFh>xB<6}nv-qOT!3MBr-2ZNz<2ml9{dT) znDYaF2nP;!a#xsd#gb{l&|GZNlqn3kw^ultYw=ZX8ITW&Bti7`aWs&ii#h6}F`VdT z%7YRO%rta-Ft|akbDr!AO3lQZJ<)(0gph$}D(0Y8{uLMxG4t{R zeD_TMYH9nY!0_UqH1>J{3z5cN!F?bL(@;qpV(=5OgM*PyL)cElYQz>xVJvHIZtf-O zeW@GR9h?)#mv`aET^CZeSCn1!`X7>sEYIfx=^|Nm=U2#q4Yk5L8GUqM~%KO1$veYh=DqY(xl=i3ymHj(y$0ma;jg= z9$cj0n8!)dkAv!q1EWBpWhuiVLs&n;iYcGt$i}A_VF6IyWC>Ip`wkA=3dLPAJ_6N^ zf1As)lbFB3p&A%zNS1_CTZ?=r(bM|H=aF6D?~FxyRq={!i4g<1qnEd6=4pC zGbr@;Lz`@&t(^gUo?~KKudV2fB?9gB@1!C=w!nOGaA}k`lR%%=3x{6E=X} zi)?|VIEBY& z-F;Yl^q>X0LW| zEEUEthjge2+=~vyYsT+O9z;f_$momp>+9g?i-O^h5I9c+^q-JM zcYGQs9n$rTj#$zjU_JEl5mXT|GJk=`{XJ7YROX}v)}6^;@n;l;_id3fN+m`9L-WNEkVmwZpg-mUkI z2!>j6F#qdbLW+__!xuB*ypqwlfL6eK83LVj7>jfT4KefGzA}WN2uEWX;1y}UCoVfk zjAuyqHTvXIy^xw*mks*&M7uF}!EvjA8;o#SvZF^~Qk$3w2i^7oq5%ApiJ!0`5e*ZwAx{41cvIb}y;q;a|HPtx8c*P=n zE3tU-Ik3rEgMhA;n3(*0Eu&(#q4rq4IkE2#F7MmBmqaEj>?a>f_?VI~XBKXdOm^)= znr~bs14+qGQFRlQkaTIo!{dhuaHL@x`2@;z5lVv;FOAc%&7kYP0Yd;WmVo$x(p>7` zs3!v*VJQ;_sr5vF9e2AF*;JT30#yUng(%n-$OP?t^bcW@oCAqEXz(s+a#@xT$Tle7 z5e@6$BQgZDqt_l5ZS6Ib3?>CY}w)OH{2Z7p`|E+Jk?!CuZ`v(nKghw zE{A?*rf~LLsG$M~S>*HY`G%`?I5EKc1HT{}=F`B5Nqk0N2)fPqQHl=u>tzsyf9h+r zcm5H9-J3Ah5z9_yZsXu)9sLV#pvCre%#nBvgi+YNiA)1zBLz)~XsB?*rFtnFy4fVc z2Z3&lRK$AB(2_?q&f-_=r*U>4MH7=Oyc8tA5sMM1;q$1Tc~|ok*bhM{+SUn#}bkcR)}8TSb)RfO82NH8s6Y z=^eaDA~n1<#0r9T=wFY3v;U%ZV440!Cwu|P@PGnRX~lBfj5mTE6jL!qrrg;Bm{1Y= zJk_t>i92}P$zza?N`VX~D@NC#c(%Km{KL17?8&4fAMOVKaiTwE{E#kq`6+uMmHRx} zopbkBCwfYU@pptUt!*R#5n98F2IYDQV+8T?(zv8D^Q-uU;+l2#|0$(izr1RvN#)jW SGFmHmN$iaD>9ms$GLKDGRthCW_TDQbD>=yCbck^5 zaX-)ZzVGkv{$2Oyd)@1g`@XK~a=nlDdB5Jz*Yo*UuY&KX$WfAAAVVM!lnU}_bp(Ql zlkoQ>30!gYjBrFCSgaM$w;#BqERVWtK5#rFUET8G(GM@$j2}AFloLAkBwZeV4%}Y5I@H?N+K;tOE+^V|QHUXtr;*p|@8A$GJ}`$`QdH#avWd_18j z{axG9QBcTw1oQCIqsg}D%h+Lfo8Ah0Gmp(7GcCC*Yz+zinH9^!WtSx+7y|+V95*u! z4GmX2g~um)-pX8fciF755!D%fo}Yv^^n|$6(ueiI20v0HBV$;GTx7alo!5oTzx@iz z1lIeHt9-9CAAS64Oz_GaTV?3ACz>WECaY^}(vKe}V%0DoqTveKVawp(Z`gBr16Uii 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 literal 0 HcmV?d00001 diff --git a/tests/safeds/data/tabular/containers/_table/test_plot_histograms.py b/tests/safeds/data/tabular/containers/_table/test_plot_histograms.py index 2e6f79ca6..16572de52 100644 --- a/tests/safeds/data/tabular/containers/_table/test_plot_histograms.py +++ b/tests/safeds/data/tabular/containers/_table/test_plot_histograms.py @@ -1,13 +1,23 @@ +import pytest from safeds.data.image.containers import Image from safeds.data.tabular.containers import Table from tests.helpers import resolve_resource_path -def test_should_match_snapshot() -> None: - table = Table({"A": [1, 2, 3], "B": ["A", "A", "Apple"]}) +@pytest.mark.parametrize( + ("table", "path"), + [ + (Table({"A": [1, 2, 3]}), "./image/snapshot_histograms/one_column.png"), + ( + Table({"A": [1, 2, 3], "B": ["A", "A", "Bla"], "C": [True, True, False], "D": [1.0, 2.1, 4.5]}), + "./image/snapshot_histograms/four_columns.png", + ), + ], +) +def test_should_match_snapshot(table: Table, path: str) -> None: current = table.plot_histograms() - snapshot = Image.from_png_file(resolve_resource_path("./image/snapshot_histograms.png")) + snapshot = Image.from_png_file(resolve_resource_path(path)) # Inlining the expression into the assert causes pytest to hang if the assertion fails when run from PyCharm. assertion = snapshot._image.tobytes() == current._image.tobytes()