From 29f8deb89698f7651aced9bb4a231edf7ed46620 Mon Sep 17 00:00:00 2001 From: Azory YData Bot Date: Thu, 21 Dec 2023 00:13:45 +0000 Subject: [PATCH] Deployed a30d8d7 to 0.7 with MkDocs 1.5.1 and mike 1.1.2 --- 0.7/404.html | 1184 +++ 0.7/assets/500x330/multi_table.png | Bin 0 -> 38800 bytes 0.7/assets/500x330/single_table.png | Bin 0 -> 31875 bytes 0.7/assets/500x330/time_series.png | Bin 0 -> 53197 bytes 0.7/assets/_mkdocstrings.css | 64 + 0.7/assets/fabric_sdk_token.png | Bin 0 -> 90025 bytes 0.7/assets/images/favicon.png | Bin 0 -> 1870 bytes 0.7/assets/javascripts/bundle.220ee61c.min.js | 29 + .../javascripts/bundle.220ee61c.min.js.map | 8 + .../javascripts/lunr/min/lunr.ar.min.js | 1 + .../javascripts/lunr/min/lunr.da.min.js | 18 + .../javascripts/lunr/min/lunr.de.min.js | 18 + .../javascripts/lunr/min/lunr.du.min.js | 18 + .../javascripts/lunr/min/lunr.es.min.js | 18 + .../javascripts/lunr/min/lunr.fi.min.js | 18 + .../javascripts/lunr/min/lunr.fr.min.js | 18 + .../javascripts/lunr/min/lunr.hi.min.js | 1 + .../javascripts/lunr/min/lunr.hu.min.js | 18 + .../javascripts/lunr/min/lunr.hy.min.js | 1 + .../javascripts/lunr/min/lunr.it.min.js | 18 + .../javascripts/lunr/min/lunr.ja.min.js | 1 + .../javascripts/lunr/min/lunr.jp.min.js | 1 + .../javascripts/lunr/min/lunr.kn.min.js | 1 + .../javascripts/lunr/min/lunr.ko.min.js | 1 + .../javascripts/lunr/min/lunr.multi.min.js | 1 + .../javascripts/lunr/min/lunr.nl.min.js | 18 + .../javascripts/lunr/min/lunr.no.min.js | 18 + .../javascripts/lunr/min/lunr.pt.min.js | 18 + .../javascripts/lunr/min/lunr.ro.min.js | 18 + .../javascripts/lunr/min/lunr.ru.min.js | 18 + .../javascripts/lunr/min/lunr.sa.min.js | 1 + .../lunr/min/lunr.stemmer.support.min.js | 1 + .../javascripts/lunr/min/lunr.sv.min.js | 18 + .../javascripts/lunr/min/lunr.ta.min.js | 1 + .../javascripts/lunr/min/lunr.te.min.js | 1 + .../javascripts/lunr/min/lunr.th.min.js | 1 + .../javascripts/lunr/min/lunr.tr.min.js | 18 + .../javascripts/lunr/min/lunr.vi.min.js | 1 + .../javascripts/lunr/min/lunr.zh.min.js | 1 + 0.7/assets/javascripts/lunr/tinyseg.js | 206 + 0.7/assets/javascripts/lunr/wordcut.js | 6708 +++++++++++++++++ .../workers/search.74e28a9f.min.js | 42 + .../workers/search.74e28a9f.min.js.map | 8 + 0.7/assets/overview/data_centric_approach.png | Bin 0 -> 1017007 bytes .../overview/fabric_data_centric_flow.png | Bin 0 -> 1271048 bytes 0.7/assets/overview/fabric_welcome.png | Bin 0 -> 408138 bytes 0.7/assets/overview/registration.png | Bin 0 -> 5609772 bytes .../quickstart/create_lab/create_lab.webp | Bin 0 -> 55820 bytes .../create_lab/notebook_created.webp | Bin 0 -> 16072 bytes .../create_lab/notebook_creation.webp | Bin 0 -> 28266 bytes .../quickstart/create_lab/open_lab.webp | Bin 0 -> 15760 bytes .../quickstart/create_lab/select_bundle.webp | Bin 0 -> 17320 bytes .../quickstart/create_lab/select_ide.webp | Bin 0 -> 16348 bytes .../create_lab/select_infrastructure.webp | Bin 0 -> 14674 bytes .../create_lab/select_language.webp | Bin 0 -> 17650 bytes .../create_pipeline/academy_folder.webp | Bin 0 -> 26948 bytes .../create_pipeline/code_snippet.webp | Bin 0 -> 31518 bytes .../create_pipeline/copy_files.webp | Bin 0 -> 30712 bytes .../create_pipeline/explore_in_labs.webp | Bin 0 -> 16188 bytes .../create_pipeline/my_first_pipeline.webp | Bin 0 -> 12358 bytes .../my_first_pipeline_run.webp | Bin 0 -> 17808 bytes .../create_pipeline/my_pipeline_record.webp | Bin 0 -> 16590 bytes .../quickstart/create_pipeline/og_code.webp | Bin 0 -> 19218 bytes .../create_pipeline/open_pipeline.webp | Bin 0 -> 30718 bytes .../create_pipeline/open_properties.webp | Bin 0 -> 16254 bytes .../pipeline_creation_success.webp | Bin 0 -> 14546 bytes .../pipeline_default_dialog.webp | Bin 0 -> 7194 bytes .../create_pipeline/pipeline_progress.webp | Bin 0 -> 20052 bytes .../create_pipeline/pipelines_menu.webp | Bin 0 -> 36876 bytes .../create_pipeline/replaced_code.webp | Bin 0 -> 24526 bytes .../create_pipeline/run_pipeline.webp | Bin 0 -> 11904 bytes .../create_pipeline/runtime_image.webp | Bin 0 -> 29336 bytes .../select_pipeline_editor.webp | Bin 0 -> 30644 bytes .../synthetic_data/create_synthetic_data.webp | Bin 0 -> 46892 bytes .../generated_synthetic_sample.webp | Bin 0 -> 21994 bytes .../synthetic_data/set_generation.webp | Bin 0 -> 18916 bytes .../synthetic_data_columns_sel.webp | Bin 0 -> 26946 bytes .../synthetic_data_configuration.webp | Bin 0 -> 24634 bytes .../synthetic_data_overview.webp | Bin 0 -> 42504 bytes .../trained_synthetic_data.webp | Bin 0 -> 13930 bytes .../upload_csv/add_dataset_details.png | Bin 0 -> 150049 bytes .../upload_csv/data_catalog_connectors.png | Bin 0 -> 225438 bytes .../upload_csv/dataset_overview.png | Bin 0 -> 502910 bytes .../upload_csv/dataset_profiling.png | Bin 0 -> 554330 bytes .../quickstart/upload_csv/load_csv_file.png | Bin 0 -> 44599 bytes .../quickstart/upload_csv/loading_area.png | Bin 0 -> 43227 bytes .../quickstart/upload_csv/open_dataset.png | Bin 0 -> 125841 bytes .../upload_csv/welcome_add_dataset.png | Bin 0 -> 353422 bytes 0.7/assets/stylesheets/main.eebd395e.min.css | 1 + .../stylesheets/main.eebd395e.min.css.map | 1 + .../stylesheets/palette.ecc896b0.min.css | 1 + .../stylesheets/palette.ecc896b0.min.css.map | 1 + .../synthesize_tabular_data/index.html | 1287 ++++ .../synthesize_timeseries_data/index.html | 1287 ++++ .../synthesize_with_anonymization/index.html | 1316 ++++ .../index.html | 1308 ++++ .../index.html | 1303 ++++ 0.7/get-started/create_lab/index.html | 1303 ++++ 0.7/get-started/create_pipeline/index.html | 1340 ++++ .../create_syntheticdata_generator/index.html | 1290 ++++ 0.7/get-started/fabric_community/index.html | 1300 ++++ 0.7/get-started/index.html | 1248 +++ 0.7/get-started/upload_csv/index.html | 1295 ++++ 0.7/index.html | 1396 ++++ 0.7/objects.inv | Bin 0 -> 1105 bytes 0.7/sdk/index.html | 1319 ++++ 0.7/sdk/installation/index.html | 1321 ++++ 0.7/sdk/modules/connectors/index.html | 1339 ++++ 0.7/sdk/modules/synthetic_data/index.html | 1202 +++ 0.7/sdk/quickstart/index.html | 1352 ++++ .../reference/api/common/client/index.html | 2808 +++++++ 0.7/sdk/reference/api/common/types/index.html | 1292 ++++ .../api/connectors/connector/index.html | 2417 ++++++ .../api/datasources/datasource/index.html | 2673 +++++++ .../api/datasources/metadata/index.html | 1344 ++++ 0.7/sdk/reference/api/index.html | 1194 +++ .../api/synthesizers/base/index.html | 2859 +++++++ .../api/synthesizers/regular/index.html | 1960 +++++ .../api/synthesizers/timeseries/index.html | 1991 +++++ 0.7/sdk/reference/changelog/index.html | 1248 +++ 0.7/search/search_index.json | 1 + 0.7/sitemap.xml | 3 + 0.7/sitemap.xml.gz | Bin 0 -> 127 bytes 0.7/stylesheets/extra.css | 46 + 0.7/support/help-troubleshooting/index.html | 1197 +++ latest/404.html | 6 +- .../synthesize_tabular_data/index.html | 6 +- .../synthesize_timeseries_data/index.html | 6 +- .../synthesize_with_anonymization/index.html | 6 +- .../index.html | 6 +- .../index.html | 6 +- latest/get-started/create_lab/index.html | 6 +- latest/get-started/create_pipeline/index.html | 6 +- .../create_syntheticdata_generator/index.html | 6 +- .../get-started/fabric_community/index.html | 6 +- latest/get-started/index.html | 6 +- latest/get-started/upload_csv/index.html | 6 +- latest/index.html | 6 +- latest/sdk/index.html | 6 +- latest/sdk/installation/index.html | 6 +- latest/sdk/modules/connectors/index.html | 6 +- latest/sdk/modules/synthetic_data/index.html | 6 +- latest/sdk/quickstart/index.html | 6 +- .../reference/api/common/client/index.html | 6 +- .../sdk/reference/api/common/types/index.html | 6 +- .../api/connectors/connector/index.html | 6 +- .../api/datasources/datasource/index.html | 6 +- .../api/datasources/metadata/index.html | 6 +- latest/sdk/reference/api/index.html | 6 +- .../api/synthesizers/base/index.html | 6 +- .../api/synthesizers/regular/index.html | 6 +- .../api/synthesizers/timeseries/index.html | 6 +- latest/sdk/reference/changelog/index.html | 6 +- .../support/help-troubleshooting/index.html | 6 +- versions.json | 2 +- 155 files changed, 51865 insertions(+), 88 deletions(-) create mode 100644 0.7/404.html create mode 100644 0.7/assets/500x330/multi_table.png create mode 100644 0.7/assets/500x330/single_table.png create mode 100644 0.7/assets/500x330/time_series.png create mode 100644 0.7/assets/_mkdocstrings.css create mode 100644 0.7/assets/fabric_sdk_token.png create mode 100644 0.7/assets/images/favicon.png create mode 100644 0.7/assets/javascripts/bundle.220ee61c.min.js create mode 100644 0.7/assets/javascripts/bundle.220ee61c.min.js.map create mode 100644 0.7/assets/javascripts/lunr/min/lunr.ar.min.js create mode 100644 0.7/assets/javascripts/lunr/min/lunr.da.min.js create mode 100644 0.7/assets/javascripts/lunr/min/lunr.de.min.js create mode 100644 0.7/assets/javascripts/lunr/min/lunr.du.min.js create mode 100644 0.7/assets/javascripts/lunr/min/lunr.es.min.js create mode 100644 0.7/assets/javascripts/lunr/min/lunr.fi.min.js create mode 100644 0.7/assets/javascripts/lunr/min/lunr.fr.min.js create mode 100644 0.7/assets/javascripts/lunr/min/lunr.hi.min.js create mode 100644 0.7/assets/javascripts/lunr/min/lunr.hu.min.js create mode 100644 0.7/assets/javascripts/lunr/min/lunr.hy.min.js create mode 100644 0.7/assets/javascripts/lunr/min/lunr.it.min.js create mode 100644 0.7/assets/javascripts/lunr/min/lunr.ja.min.js create mode 100644 0.7/assets/javascripts/lunr/min/lunr.jp.min.js create mode 100644 0.7/assets/javascripts/lunr/min/lunr.kn.min.js create mode 100644 0.7/assets/javascripts/lunr/min/lunr.ko.min.js create mode 100644 0.7/assets/javascripts/lunr/min/lunr.multi.min.js create mode 100644 0.7/assets/javascripts/lunr/min/lunr.nl.min.js create mode 100644 0.7/assets/javascripts/lunr/min/lunr.no.min.js create mode 100644 0.7/assets/javascripts/lunr/min/lunr.pt.min.js create mode 100644 0.7/assets/javascripts/lunr/min/lunr.ro.min.js create mode 100644 0.7/assets/javascripts/lunr/min/lunr.ru.min.js create mode 100644 0.7/assets/javascripts/lunr/min/lunr.sa.min.js create mode 100644 0.7/assets/javascripts/lunr/min/lunr.stemmer.support.min.js create mode 100644 0.7/assets/javascripts/lunr/min/lunr.sv.min.js create mode 100644 0.7/assets/javascripts/lunr/min/lunr.ta.min.js create mode 100644 0.7/assets/javascripts/lunr/min/lunr.te.min.js create mode 100644 0.7/assets/javascripts/lunr/min/lunr.th.min.js create mode 100644 0.7/assets/javascripts/lunr/min/lunr.tr.min.js create mode 100644 0.7/assets/javascripts/lunr/min/lunr.vi.min.js create mode 100644 0.7/assets/javascripts/lunr/min/lunr.zh.min.js create mode 100644 0.7/assets/javascripts/lunr/tinyseg.js create mode 100644 0.7/assets/javascripts/lunr/wordcut.js create mode 100644 0.7/assets/javascripts/workers/search.74e28a9f.min.js create mode 100644 0.7/assets/javascripts/workers/search.74e28a9f.min.js.map create mode 100644 0.7/assets/overview/data_centric_approach.png create mode 100644 0.7/assets/overview/fabric_data_centric_flow.png create mode 100644 0.7/assets/overview/fabric_welcome.png create mode 100644 0.7/assets/overview/registration.png create mode 100644 0.7/assets/quickstart/create_lab/create_lab.webp create mode 100644 0.7/assets/quickstart/create_lab/notebook_created.webp create mode 100644 0.7/assets/quickstart/create_lab/notebook_creation.webp create mode 100644 0.7/assets/quickstart/create_lab/open_lab.webp create mode 100644 0.7/assets/quickstart/create_lab/select_bundle.webp create mode 100644 0.7/assets/quickstart/create_lab/select_ide.webp create mode 100644 0.7/assets/quickstart/create_lab/select_infrastructure.webp create mode 100644 0.7/assets/quickstart/create_lab/select_language.webp create mode 100644 0.7/assets/quickstart/create_pipeline/academy_folder.webp create mode 100644 0.7/assets/quickstart/create_pipeline/code_snippet.webp create mode 100644 0.7/assets/quickstart/create_pipeline/copy_files.webp create mode 100644 0.7/assets/quickstart/create_pipeline/explore_in_labs.webp create mode 100644 0.7/assets/quickstart/create_pipeline/my_first_pipeline.webp create mode 100644 0.7/assets/quickstart/create_pipeline/my_first_pipeline_run.webp create mode 100644 0.7/assets/quickstart/create_pipeline/my_pipeline_record.webp create mode 100644 0.7/assets/quickstart/create_pipeline/og_code.webp create mode 100644 0.7/assets/quickstart/create_pipeline/open_pipeline.webp create mode 100644 0.7/assets/quickstart/create_pipeline/open_properties.webp create mode 100644 0.7/assets/quickstart/create_pipeline/pipeline_creation_success.webp create mode 100644 0.7/assets/quickstart/create_pipeline/pipeline_default_dialog.webp create mode 100644 0.7/assets/quickstart/create_pipeline/pipeline_progress.webp create mode 100644 0.7/assets/quickstart/create_pipeline/pipelines_menu.webp create mode 100644 0.7/assets/quickstart/create_pipeline/replaced_code.webp create mode 100644 0.7/assets/quickstart/create_pipeline/run_pipeline.webp create mode 100644 0.7/assets/quickstart/create_pipeline/runtime_image.webp create mode 100644 0.7/assets/quickstart/create_pipeline/select_pipeline_editor.webp create mode 100644 0.7/assets/quickstart/synthetic_data/create_synthetic_data.webp create mode 100644 0.7/assets/quickstart/synthetic_data/generated_synthetic_sample.webp create mode 100644 0.7/assets/quickstart/synthetic_data/set_generation.webp create mode 100644 0.7/assets/quickstart/synthetic_data/synthetic_data_columns_sel.webp create mode 100644 0.7/assets/quickstart/synthetic_data/synthetic_data_configuration.webp create mode 100644 0.7/assets/quickstart/synthetic_data/synthetic_data_overview.webp create mode 100644 0.7/assets/quickstart/synthetic_data/trained_synthetic_data.webp create mode 100644 0.7/assets/quickstart/upload_csv/add_dataset_details.png create mode 100644 0.7/assets/quickstart/upload_csv/data_catalog_connectors.png create mode 100644 0.7/assets/quickstart/upload_csv/dataset_overview.png create mode 100644 0.7/assets/quickstart/upload_csv/dataset_profiling.png create mode 100644 0.7/assets/quickstart/upload_csv/load_csv_file.png create mode 100644 0.7/assets/quickstart/upload_csv/loading_area.png create mode 100644 0.7/assets/quickstart/upload_csv/open_dataset.png create mode 100644 0.7/assets/quickstart/upload_csv/welcome_add_dataset.png create mode 100644 0.7/assets/stylesheets/main.eebd395e.min.css create mode 100644 0.7/assets/stylesheets/main.eebd395e.min.css.map create mode 100644 0.7/assets/stylesheets/palette.ecc896b0.min.css create mode 100644 0.7/assets/stylesheets/palette.ecc896b0.min.css.map create mode 100644 0.7/examples/synthesize_tabular_data/index.html create mode 100644 0.7/examples/synthesize_timeseries_data/index.html create mode 100644 0.7/examples/synthesize_with_anonymization/index.html create mode 100644 0.7/examples/synthesize_with_conditional_sampling/index.html create mode 100644 0.7/examples/synthesize_with_privacy_control/index.html create mode 100644 0.7/get-started/create_lab/index.html create mode 100644 0.7/get-started/create_pipeline/index.html create mode 100644 0.7/get-started/create_syntheticdata_generator/index.html create mode 100644 0.7/get-started/fabric_community/index.html create mode 100644 0.7/get-started/index.html create mode 100644 0.7/get-started/upload_csv/index.html create mode 100644 0.7/index.html create mode 100644 0.7/objects.inv create mode 100644 0.7/sdk/index.html create mode 100644 0.7/sdk/installation/index.html create mode 100644 0.7/sdk/modules/connectors/index.html create mode 100644 0.7/sdk/modules/synthetic_data/index.html create mode 100644 0.7/sdk/quickstart/index.html create mode 100644 0.7/sdk/reference/api/common/client/index.html create mode 100644 0.7/sdk/reference/api/common/types/index.html create mode 100644 0.7/sdk/reference/api/connectors/connector/index.html create mode 100644 0.7/sdk/reference/api/datasources/datasource/index.html create mode 100644 0.7/sdk/reference/api/datasources/metadata/index.html create mode 100644 0.7/sdk/reference/api/index.html create mode 100644 0.7/sdk/reference/api/synthesizers/base/index.html create mode 100644 0.7/sdk/reference/api/synthesizers/regular/index.html create mode 100644 0.7/sdk/reference/api/synthesizers/timeseries/index.html create mode 100644 0.7/sdk/reference/changelog/index.html create mode 100644 0.7/search/search_index.json create mode 100644 0.7/sitemap.xml create mode 100644 0.7/sitemap.xml.gz create mode 100644 0.7/stylesheets/extra.css create mode 100644 0.7/support/help-troubleshooting/index.html diff --git a/0.7/404.html b/0.7/404.html new file mode 100644 index 00000000..fd500bf9 --- /dev/null +++ b/0.7/404.html @@ -0,0 +1,1184 @@ + + + + + + + + + + + + + + + + + + YData Fabric + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+
+ +
+ + + + + + +
+ + +
+ +
+ + + + + + + + + +
+
+ + + +
+
+
+ + + + + + + + +
+
+
+ + + + +
+
+ +

404 - Not found

+ +
+
+ + + + +
+ + + +
+ +
+ + + + +
+ +
+
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/0.7/assets/500x330/multi_table.png b/0.7/assets/500x330/multi_table.png new file mode 100644 index 0000000000000000000000000000000000000000..0ea093364424144403e5cf4622222ef4feaa8317 GIT binary patch literal 38800 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

4EmxdoSXXl7)LE!%C;8WTkh>D=Yr$;` zZJ3>Cr$f5sU@)+f@yCTG{4>YuFk{{+Q*QxE1vx57>LCZGsTgLaud_fedJ4(cM^<9f zii!>sI8IgXbi`Sd)MUSfsjUk8*0%A?;1bkG+Tu(%*iRI_4D7msT>j)e@6J<^zIK-D z6EcZjvd_JkKKxWYF)?{R9Om$rKZkd zqQVjQ{4+V>lx23q4G&UyD!K1|_%>a1L3xJFlm_)c9equO$)a4V!f!z?E&uiWc`892 z@U_)hZi)#^LrK7eEeX=NwjxX#^Ua+;ZTR1(m5%8sAw18M%{SYW@#DucYt}3P#*Q5e z(tx0qz-HYYPl2TG<7qI6P@p1Wx({bE2ESqun2u{m-u?=tbIN%*BIFcfRVg{rdgX__MtGKTzgiY8x4#!u+Tzj z71G?uE-G9Al!$Us+oUsJwqrNA5#RpXRkJv4Jbr#oACySx1114e93c@`;t4(`DCvE* zpnp-z`C|B=Rk*={GyK&&*cxVP#`(=Fb8%a|Jqf}C-&8SI?daKM-+bjiywXrFgDMB* zN};PFjg5_n2!jRG052aG7E z!s%*-LKPb?rFq{2(Xw)KgWH_O+kTH^-{*AU4tbsOCJ8!guetj!x1-#9$=`Qwx01I{ zZT~i}P*?6aB4|@1t^b&YHZ}yAFWV2y3Pp)3l$OE)haeS|h)D395QCV^dt=&{IRP};21}PNG38}tWJ^mbD)n`B1~4@>HH;ZEhWqZjkEW(39Xcq&yyhgV z;A-_%@hqgRlFD7%-E1PDkq8=%LZ)=Oxj1H5RCH?gk=uW>hGY!g#_N> zS2)TO>2=pjr}y03#o8l5X8uo>f3~4kbHO=3DR$GlaE6x&=`x^}g(yDF^P6E)?eBZ;z759OnBJEAdw4-;X{FOBK_X7h$pbr!QyQX-3 zR5_>;Z!6mn_`WEYJK{98XoB4qrB-|Y{mpkX&Tmgvmmp{|;hP&{j{OAHODv2< zwzGcwWe+^Kb4@qJ$aS+r=8lS-uw63A(_Y8EY8>;TT(xpTFx zt<6?eRtA=)XFgRn8j?3(T$py#_ex5aJgvOI9y=}XYC;d*byILlJMAF9CPKmb2-4-> zf^F*VYkKFb-ec$-y-z)^dBR+aSQKPH2=uQTqK0^+Oxn`T6IAtMtUT|F#VJxr%~j>w z%iDOWI!@fR7l%`6=(lG6zVHA0F-uVt*Vj@@|FAnfA8+S z1n>%LZc3;N(}0GUAfBRD!02{3oDU2a_>BvXdO|wlLERs&!)t7nNO{Q-s=Q^5_rAg5 z{BzGPR`Yw!Brk9#!;tXdHxAUQa<$j0gG6~yg#njUZi)$>;YkNXSzm?AfWhLJN!zCE zJ5smI#Gjrkjn;}-YaGWhp69WA`Enlt7>_S+c|d9LI6gaU4k|leVU&#!7QD zpKMqo@g78#Bf>frafVqXrHVy}JKf}y6Tu`Cl)d;2xP?T4WR9e{IrIV!>mAzT2=O2p5Te9I0>u7 z9eLj>r-|?jL2vL?u4?m2{#D|(>63$F_}XvUdbem!Lw9oXf7y9I$zNy1c)-Fn+W0*F z>If-?n5PJosnO#IQmwxP{6dv4Y)09s``vYjDv`4IM!-1GiyZ6vv9-vlY_bg;;f#{T z=O!GQ6)pgVV7Uc@H^m8J+KbJUY_b>!ubh_1XZk6S(*{(zVB;!dAX!gDH4eUh}hKFYGTqZYC9t$42rs$ z_rWsT(;T|z5t;J^E2GU9OoN&i;*}HV+Ud1pm`QGLs$B(m$26?@$JLI*9kqnw5eoD*$eSaA!X_@R>*1XMyzQ%f)uDWShQ zS(BwA5m1k<6@CJ21Y9fdpoP>zD~xuPNgn(Lh{O~xWex#8@YViD5Sk;a(U2l=hM&{a z{R*Gkn+#0|=i^W%@F!IgQw)eLj9@lNgF`U_)P-=)v!PcP(m^bV|kwd`ICo4oI5Li*oTaXdGsBKl8N<||x=>W#p!Q2Rcz=7hRR$B>(Rx!ob& zc7SnHBpBtpj=}o6xr&^t=#~WX?S2c15)=!Zm1o=$z=|MN4^nigiq2MnXlme{V{e&k z;d&>~h7CLqoCW+5p%UdXgyr+cmvQf`7S>dB{S6L?3Zh!-8O{X`68OFGx!)JDbB{cn zcbADwp5b>3XH)A#MI>b?S658JO`-OlTE9vl?fw(~h~_k%0CjQX6l z_by+{cyqXeI^~GkZ!274(NhHeD`MDwL1T~G9)8pbsDcs27#KPlmdr7Tz}5`J@{0kINM?1I5;&Y;4Pe<+;2MNR;6fvTe}fS?wI64%rV>b2-w1*XSL`JOncFT+ zDKEw)$P2LB+i!F1vB%2%`SXpn)+Ccj$M^k6DwQ&iJ@(kv`|rR1bXR1<)Qo@AcJFO? zpM3%%xEpLt$q~mB-RnDe@Yyp@KRt2({r7)65{dZfbUOX!n{STVaHEZ0Fd}2J&RYcn zxwET$N9pzTk&J`Uqe}DPQvh=4wThVbc-_H{m2h1xgE9=MB6eiTr z*8p#ht_YlFuw^PtK>7EM3+;bjAK|e5!otG*kV7U=K0HaHA(DxWbREUp*jkQSJXb!I zw@2M(w_U&yM<36 zqfoMmtY)5{v9j1E_h~>|To5nU>Wx+8T!igZb*gc>$9g3E%(s&Y#VQwA_&6faUxi(!I>98i?*djgRrCsiB!rWF;iEDO zR^V*cMYrFcQKLoykV>V*7-N#jq`0o@e)F5(Jjiw34b!i`O!JiAlm7AxY%<7^(6!FKb%<%nQWjyP&UvCptuv^|w=tHHlk;VR%5tMV(*!G>l$ z1CLk5`nw^yH|@7t*6UsRq?i*d_mwKxD??!W7xqDGUoBuKK?zYWX6Q) zzzJ_xU<$}`lrw;j$~y0-=hj-zIp>@~6_u6aNT*2t z-=n1G%)x7E#cylFZ%g2{w341Xhs6Iq3aKO&6&1Ujciwq}v+d9z`Ao5YhF-I#?yH`T zv!?IuV*Wm%K<{VX98m?OE!LaOHqmK_$g=SBJ95y$ew!;b?ixPj^zL)p!! z{Uhk#fMFV+ksyyb_)kX&4%!!2@{Hass;I8eTwhaClC~iu}1xefO8bK1-TyV z#*QDuWSr&tn*4kg?K9?qww@i?+suD!nwwp?HDVCJiy1^&AK^EMoP%&1=$S@%KcN=i z!YN6Z+>JTew?2Y{h}i=qrmFw+(?w5Q)i0cBp(O=Pfw^L-Am;&Zqx=EOu5IxkE9gyq z;MG5sOetDGEyjg|6|Mzd5cGUCESfZ8d)f?~nAt{EnKNe&yY9NHdY*^lI7FjS05;rk z!?H*uvYs|Klm7SvY$}C%?ISczd)QfkeEU=s@T zekO2U@c|ALCPW zRgp^+sjmGDbbu$A+rK^;6eAZGMPEZWRiPQ-MhoLTKz*NoR`zXdRp3bk68J&;g8CUH z-3Fh<=6hb*Tz!p*Ww$j4b1PWgSweeXNZc!B!!Dn zZbi9Dq0uwKwE1szNOH5`h8wcePCGGg-aM(OsGy~#1)It4mh zkw_$xJB#f0y%p;HWqs<+KKtA9wI&8OosTil2UFo3dOz~!=nAZQfEfaMG;q6XSd_AG z#jb~Nuo^^Cv@EX#s1{^zM2|%n1@fH2w3j_=!&RI+8q(z3GGB9TZN;2TWKn#|??w~PmStj$MZU4?TI*%j4WQMn7z#$j;} zRXBXniud>1-~TCi(2I@-h5?URs{$+rMP|nA>~z=b=Jwlfr=_JuHrs47mM>q9wH7a< zg)dsPDB=75l1NGEI_`QKU_Sf+Yf@+sXO1`y#&vPl+W;vo!}q*J3l=QM-V7Rv1OwjA z^TM6yZL^;{n}74yD>T=e^DkL}*|^a6`P+8hPj~Grw2wmD6lzy*_SfBhx;u~j?{lH8 zdD|B{w-p_KO%iJ8{ft;aR1OC`#Xjm=%cbKF!=Sj51{k4Mjzjqea4zs4V4_nk7YO=> z>js&d@4fprZod8=?!Ie!*2QbF!sUYOAwDM#s}BsxM;|?**r(hh->pyZV*-1GB28(> zxeEA$!fC!6xk!x+QuTSH+~v`VKGW8HV7~9$In7Nib5p5w^U@{D<|dQLIlz3@P!#jD zsKfM_3kae_P`)q1-$6%#PPT63uZT8+a8d$J?qvXgwHB*33UVayG`>3*vHbjJU!A%0 zo|x=sa-$g1tx={B)Nna>_);ceiHO2fr}v|q8-oH{_^zNTV?h1{>>$cnVtBkVlkBIv zLBdHVoy0ZQT!Z5{IF5rcMq{y41fP@K{dOe~6tMR7_? zaEA^hw(s{KUP4Pt%YSaZ`R4lUzI6psBVQ}dzR&-hzg|{271E^hm-qIs^S<-(RN%pTZ|8w~Ztt+~yY8G07TjRo@JfMWmk#1vYJ#BA zfiBbm4yaF|7+l|~7jf?e&IL0LtA9q>z_>0V!ef=I=PWmF+&3Aw>o*zq&7Dc7(m0MW zfJ7p$sa>|5b=Dcps8J*6&yclgz~4$7g4CWqz!>Y8s}O#H@Q@KbMc_43DNyB3-m0UXn&z*Wosl%YljNa1KJLqTCPC&a?Pd@qN zpVrzXk@4T6^te-qZnP1R>cK>+2NT_JLrRZ3ndrDZ@vU9_;fEhSx?sVAlrhEv8jVJ~ z6{fuvl!ew85lpspXrX zYU-iMy}wWa*9YGmDd@6w8=C%AJ^30r#; z^<{-qflCx_#M(=JwXW;>as7wd7zCTKRGzH z_bZP^pT&_tAf4i^{4sp>j1l(;g;%XL9~rS+c>da#6>fDo!-E@KD47d`H#3jdePkPe zs}w2`u8}m&egvl2uG;l9pv43;+Ka0rjHplVts<9#z9Guh2uuCJQEXj`Nh`4D%$hZe zmX;RIIp-W*zI-|5<>l6K9Mja)q`vQS+G(f#=gB9ZymiRXq34zyH8B?1erLQ7-U$*3 zZMGHeW?Q3?NV0axl4)n1b=I4JI*y~(S{sQ(v?rnB` z@;a>Vw}+Q?sA(SjlV5P8>pFQg$|<~D@;pz~eSLSq z7Lk1sz65Sic*ckyF6b@T+52mY_OB5Vx&LWHxVF}=LWt{oGo z-)l(lSXCV3I#|zsDezMfIaifGIpSO^hWo8&Jx5~%g(=q_{vHb?M5I0q$_N|bOptvL zeFV!*7?wX0eFj9BkX-o(z}3I~1BhU7Hx*UB z(TO1U3A)_(EmoC>?!SWv@4I8Aj&;|a(*Y2n1>|y|)WXj(8jCq_%yB2K)c%Ua_v;fF ztNgSv38M9CxD5CU=ufS$=d0Q>70bQALwyt3)~|Qmv?};JB`6SetYCFNM5cpoC8Fos zmj_*gusrHnjKWE)Ak4EkL_qnWDxVnRz9Ax9_%)e3?~XGviBhcrgf|4v0Y+o^1Hxzo zZlvA(KE(k?!9WQP=ZJ8&z;%x052CaJoR)+$dU^cy*Iyri&p-bh-}kkqrbbIkOI1W{ zX=$k+iA3zpH{V>h(@s16W#-J8r!H8q@TvOxhL7v&>OWktVBu3Q&YXGD?z`{)=bLW2 zsXiW$+juDC1%z?-edlc`D6|VL&u>?D zdHyY%UEcBYs?+t{Iu1nID>PEv9QwT=)X@8Y0}nYYh_c4T0?&-uxa7=tU;Os+c-e{1 zj@d+8-KaXM=Mgx(1s`b zB{0tlJp>o@(^*t@yBspPBzo?Lo|fnLPEcW4X)pVtx%TDC zz;si1rI1&CF+JWxfxa4i|DeOkUS0+lMF)fIxk=s9qocmvN71L3l$2k!ELQ&B@I#RdG8fEn8(X_U;@L_T-VjMwl1YCnTo%QAEm^Yj-UZF)ogD4*GBLJ)m%oof+qLh%eLrNZ4DH%-_PsHt!@Ax5 z9zFe5@puetEyiTc?7^R70#keTvoV3$J(r@Y8>hU`DQ+#|kV6h(d7_HSNHZ#$O*%1L z(fxrRDh%GFVcDY1>lQDmO150R%c3veu8Wohk9DIw{9xY-bAaj7r}Lv9O#s&-;{98R zZUOvUL_hbf`J^tnb~wppIjAwoBb71AqY+xt-e*?%G3a>+e-`+?wf-Z#EZpa)`<8ZSoLkqkRcx6mz9-iX=$lO zqfs@+*t)tp8XFsX^uCTnBCNmOIvti53e9R?-(`cb>-BsxolN$fE4Zy}ftOA@&vSh1 zSZ};zOnAF?-`%;d3a;xgcK!7W9<#gS^frp17=!}7mmGM=Aw&=!diX&OIOuSkVKMxr z$#q0I1ULx6RpgOT%?XSEqDyk2*x{-HxTlhbAoA+O~Thza$LhrtNI!7Nj0o90A zbczufjdH0mdiJ1Lx;E*pT|f1~jY%G=usE&@zF(`VzY>uXfD4Rq*8`p6Tbm$W!b8zC zhc>QQP>vtJOP{v!wo((A5>+t?D>kOPqViJ}$`mdHeNGV1jrrKLVM3yB#u3(79P0q= z7c7ydA#{l8j==n`;DCjCg)0Qk(x3#Z0iG224MIcAD9r+s`{1~bKmIsl#*AUyxN*A0 z7F+P(gAell`|sOLH{DbKS+HOM)z#HD8i`tC#0J*vh#fv+gav$K4CUqJI&R!J-Ezw< z6~Gv?!eK^!xL9wEsA@h5ZVZMPJ=JA2KcO=%#_!5k6pt4Kf<>^7zkO@zK(>0 z`X2c1frqmHfro$v5p@vVX4uH4tAKw1J1Ja;=#M~d+G_dY1?x38mU_mV3OuFK{E1@u z#tLek_ceF^V;YTZCHTq$MK3~S7Zp8r3$qMVIri8I#qQRX=8%j7oJn~q@P7)Yi7-N7 z3Zh&4rExsRaDRFKk58vn!F|L*V98b+fv}4xH!FM((My0!MOo-;1f=h@@+?>X_S)>* z@rVooeKfp{d(UJaP*8)n&PZ-rFvz(PG@%HSTIf|+T5mb!lv6rPb8Wx<_Bvw32tDYa zgLK0UH&nn@53W|nb<`M>4UN*OszD0az4qEm4?OTdUA%ZPb#-;x*4EZ1X5W=H3HLVp z*{WoI>&^Nr%gQFES=HkVM8YRQC-y(^klY^tsu6_o)|%^3`K!YEz!ks=1|9(F9)I{A zj`{R0e8WBh>!A7%L%ABKZwrm|mYVVwjP-CF_iYK%s!saA4%tr}2SeZ_x3+OeV+xF5 zqYfY+f=ojB6y&dB{ev1)Rs`X}^8V}d+v5f&X-LM>s4!^Q`=6dAa0AGvU?+$*Pat?G zoRmy4F|qpdUqENE~#7#ZI|zu*WEq~?Z2mVu=%;uS2l%gPsht|Z*Sp2J!~*4>7!IR8ltM!Ew!NPX*f{gFtTrUNnyT z7cZ6I8;e?tc0NZRJppkY)G@|bdp61eh)h=H<2tXLf8F_~Vt4r}^KexZwP4dWDy}&d zI2P60MY+|ACcfEx6falY$ze@>V>`V)?$VMdqr9Y9-XL&`sy1%1#bzD0ZEI@-s1lJ!f!z!3yF205(|5gXccJZ9wBMep!>wwYR%W|Z zZTD&%`vu@&pgtb&`*P4L;%l#=f9JagbHH8)1w-E8`CkbfBr1O+D9N}C^h^ssR(Mg6 zR1+3om480^K;EA8U|^79AMpKD(T7TR-Z>qNV0>$_+z5OCdYOo=sg5*p%n7Fyw}GpH zCQNW+xhAC%nIgjFAU{%(D-|8~YWY2Q*602{(AIA+H&|DxC~XF)*fipt3G!EhqAnA~ z<52}q)X)^~KVhE37Xxwx@E<%rLNyfTYZ)JY_#t0@`DIYnHxt2_A3Gj|c?(xvbyb&b zRkcA?AEiJBgT6Gg_jdc*_?~j?;ot>T8l58@&$@%6pnOn{|_c?cF=eg%O&v~A$ zGjy%GeSm0`1yjHD=p%Bb5C6~Yf%x!TG<1*3^apq zi6H-%Pl@`e^;@sbFJlG;B7u;JvYWyMz;xgSEAl(Bnz9j)5KidYxCz_l&b=o8InF}3 z1vn4*2MQNmc3%o>NCUqy#+<9FtMb8vHZ@zEm;0d~I~rBN7)!F<08ACj5h8pVFbEF<_dImxy$e6~ zxnqdnp$ZQ@aCg79j6!G>qjEHmM!Cm`?TLys8o1+UKjFr&y_YB3-ut#-zX@DtM8EW| zStsB2+H1G*`PrxN%LTVoTFmw0*tRrx*F;fkz_Tx6ZY*$5Y3Zr=#=RSy=C+QHJQZjhW64wf<+`Y=F! z0Xl)lfqR1>Xig@Rj2}O~GX1Q`RV%=uA3Z7;UP}lUByFuhMETOjaC$4jRo}S=$^MwkB%AB|Tfh?{!i5(&g+5k! z<<-|9H-`??=YEb`L31PYclE2Is^l`b^*E{pK|m}P>zUsTf`CXQLJ$Px6R`NcPZ(yc zNQI7E#$>0jthGH$;_~IJwLRs6Ap2d9M0%&O^7VrtKvhewfA>7EPuuBqx{nz?8==kg zw3Cme+Y5zK!>*|41BmFAda5=e44F;q#4+UpcqD8hxl=kJK}z3ppb&7lqXZH?j6 z9sT3L|Ek8r8qh#ds0vj+C(4NmFROB*vF3F(76DF3ZQ^|68iM^WYMwdF$^0js@9(GM>c>a*Mzl zz!f5Pf@p>lW}jMV5jPm)r%)^wvEu3dnhp51E9mvshC2z0)sQ<9=FYMJ08WfaL_t)m zdcXF+sfJ&~Y7i@UNkLR*8svK5E5MHhnPY_4mrqWUiCVtUy>aKtit!9gE6B0D2x6oiDT9O8LgFYrSk zfpQ+oS3&*&w1w$x&gwt^@qX^O^}a!s`KwbJ00=WtQRIs#i&X7jvwxPO`*z;3;8rXV z6uLmK1Y(Mutzse~@RhHOZKZxp3mn&(K~)F>gMoiz^-PdX5xGf_17Ge85rp4X`7Ef1 zJ3f(p5DB!?Vv5I}yeu>qA&i1KsbYS4Djuc_+S5dMbHZ6yN|^2j6i^6;PT z8}hZ1kq{A!A4$!UY(kb>J>zwCcek;}^7cyKib09cO{1wrv&*iUKn^wN?4KsHV}jYU zPph(ugP-1w@u0`gN2)1K-`f+CRalcnHKZhA-a}0dz-&Yk;#rex`7iD6e+M zGpJFHYD;ah`SL>M&Yg>55P}K7Q405Y&>C9HMHhXe(zn^Mp(x~nSmYCR_StbbhRc|K z%s=qJkE0gEOQESaz$8TuMfLLt6F?pUnu6|*S0afTo6G0J!w)^M{x>WXG!#QbPXhk! zsd?IBiALkVCJZLNo-A0ffZ4N8LogWOf30$@!g*hg>;GBNzeyO5smyHU_)KTkB>mg! z2&i;~>V4lxxEMIw8g>TzeS>ypJUvE~GZmI1a;ju9FB9>pNk&)`4>)0jg43)If?_d< z+JkaQL>|gMqd0@?=7T!r?HDn}q(VRNki&C%`z#U@2-wCdM}ZzALKSeos@%J-W9l2@ zYnNjpacpRL?9m4|=(_*eran94W%PZ(2T?sc6dMS-(X*+IBnuYI=S#Cs!2+R#_jvf* z0N)bKnd-}H>ep3T#1Z4uof&>p9YqKOjQ1-IgN2A(2IfYPR$xzr+Yx>#*0zW77^2Ei zbXPDuM0Rv;@eyll5jj>>?=@mpr8Gs;MHg54wmL?FyoJ3md}`rwhhvzG8HIi!``!+O7B+zk zX*Z!nW3sS<3iRXV>809$=~eXTqii8bX8pS`UJn;1@GuRf|G7Yor~!%m1#!p^@*Z}O zADOU|4)49>!N{*?8&mFSjQZ&*LzjZI5&fOwSd-$|BHee_@>`p&U-1w=D*s&ip}M0& z<}a26e=4sGs?Af!)d9N>606__{1+nTz;i0c2BOn)?2;wmKJrTl*wvHqZ??;?OKN6w8$@}D4 zsLNuhf-x-l8{_e}oOyjf;q(` zEIB1`oXx3zba<&@FaB?F4;J_tm8+6F=~eQ=-*r7lOa!$l{8u`JQqfXO&=~U@@5gmm z+;4czJZ-i5@!MGGtrEn3cw=ll1HMhDL$IaFQ#=Y7tmTNQ z^Oe$igI|YCN}o3BeYNM@rz77>x^2Hx3T}&#$vXRt94j1xP@_GWpm+!{itOC7=D)^l zO&r5Ik??))9L^Kq^_M9hLlZtpNmhl-x2?PLMh9(R#Z7@5|nPp?oC_Rii7#fNL4a~K+IF`JL}0w2lyroosCOeauPR>J4fqTtHFyQ-^h*qyBz z#Yc=4;#hgGtEz9DN?fI_PNI_Fr-9Gku1&CZ@L&;AbzJ<4#hjzbo^#{);dC5uH|l$$ zabsMz10|fc{c=H)!d&qGQthn;QcrfxOpNa<)N9n z%e6#gjYIwsq7{CeDs-hj^~zwi;;N}#Y`+#!M@%%%N04}9^ho-`6Egm5P0HtBFo~UL z?$zRGP2Cq+RUt)J)m1)}r}BD!pt8v#Hlbwdwn=A~o_dWAf?+1&xgz+RaQ}>P4godhw>BJE zNitdLpG5g@(D#`zh%{sBa)hsEb0c6uO6HlU2~!{pG%sjvWZyDLBk#LFINUFn4aDR4 z*b)Pm%x!~@oBK?*L8rH-7rH|#PHmnb3-kEn1UejL&xX1h0F_m(!8GyEFM*xHg|wuB zjAs{<#x8mh5DVMxfwMnORfE!VGdC^3q$c^F5ir-i0Abm6n|7Pag)-sH^g6zZ*RQiC z#1;nY0e^q~NlQai*wqZPuc0&FrAwP5D5^UAm_w`o&E~E|jqPKV53g~RYrR$5NaeV>h7ZE+{-^h0R&>B zxCF-0GlkQQE+D;N@08&+dc&kK@sa`h(B&9-{;YanKd6fizHrIUzxvW~^7C1J3rTuz$#eTRF+8{SxXogk_!R=UMwJ$nz}8+{WyqC z&0+Jg&{bDo4O0Seenh8|h+%FM`fgS(Hy>v`FzA%r^pw8R4t^bzoMDT?J*Eu(X%(oq z6J9MV!;TH6J8k=QQXlt}tE^abeOp+Q{OqH1<)e+09k%d$ek;ghP+wsTal@$i-q4%5 z7Pu9L5;M+>Jh(vpa8)b>=#`hZjdu$&aK^ez_nj>d7>amPPPqcdatYIBU>fGPODZ(~ zY35vDd8reP%Zw%qg@pA~uW@pEpE%KdJqvoq9{mWja7l%h1BVZzf`zCBo!6$J2WjPh zP_bGoCvSPtsc&VnUaR!WY|1-9j(+7wgRS)^bm%TrC`oaxFOhWQKF%8jrjCL)Qd2N5 zlvkzLCe6WnUd7u{iaPw4MoB|J+lj}!X14&GJFKrv@i;|k9o&#i@TM1}sqiJ<-1Y(5 zh{gPuyq(#@kJ5Y^jhGF4*;CyhJ(AyONs>LTyQ8JfYt;fZO=+1JG?Dc|81HGsQmP+d zD07l4+%m115s{NL&QGv)pVPG(ylxweSEoDki(j*7mvVkKfhN~sqZ88ETVXdioH?35 zC_cq+=h~e3?bUv8Li%cv%yt6~6sX`mg}b4`Hw`_u5@zMHLQ5>+vIxHf7}`_Kl)h6q&zreyY**ea&Ipt6=iEbk5xa+Sb>RRP5INbr<2(*me@@3`R2S zbfrtdv`_kyL1t7KF=I-cTHqC9K4Jc)S?224Y9scGJZ~nT!BsPB6swtW_-DukPK}NJ zcWI!2*xq5bN@{VrrKF*rv59(U@*oW>C5ubQPx{Qii(cyAlPz#2YZ9o+(>sA8s{-d` za@9e{k%r=X(DTve@NucEj6ea*YNH3OHBxHO!}yTyOqXEjl61- z8~LWc^BRZ)mTIo##u+>Ys?MOTs^(U#nSiAEg~~GIqLO4VIzM+Siv~9)aec3QIYUdd zxmI{wKc>Lglj(N696prc8)-MxIsc5D_bHrjC8nDUTqH(o(i%rlOG~2XfhlYHIiDV9 zxZZy8R-6U;gO`AkdfX_rtuca;?5D5s^aFe1rzdRUMsjB2?|^AEu-|}h1cf2Cw5w|~ zQ0r>&d0yL$##u|R5{6byNvTxB=@TVE9W`NQPmo0UA?g80i^FBQa=z@yUP5-Yd>46; zpfi31d%(DNLzRl6X~b++(dvqH1eOjkK~)*&H&Xp{{minCyEt5R3AJQsb z)0+m+DE`((xAUFT)h-r1yWJf`5$s2HuS37^=H+VY!%v>E+q)rqPa#g%MHrC1vaHR_ ze6?PzN&s}08;yqGkgqe;YaakAU&?&;pT(C8x9MVy<{o&?ktftY>$q6WMza{v!kL^X zS>RnK$#Glm_r}LE#V8+h>1EFZu;mRuusVQE%2zc~_if2Zm(46A;mWQym7OWoU<)xo&gEvzc1U zy;i2#G>ImxM{qpScOSiUMGJnkp)!`V)-Vx6QP-FwC~4HnYqxuqTxXrM$6}vUTf>>0 zJCjDHTo8lnP`Blbj%RvvaLsw=BLp|4gPNSyGX>$QK9t_!~HFXBCom6wuwY=EgpIVANjee=-{*)E#%%xoW7$`zY@< zeTiIPjgH*KJi%LosF9aEx$=HJDu?UdCqBcE!qaP@=rDt3T>@csCWnBpmSM5K!|Zoa zgKd-n`RjBZNd)%1$yPO!g}H>=Ab(#eYCzqas+6}8Ad3ha)CUkDdd%r23#SWyteDj@ zgL^R~B)S_Lv$LzG;5ii)5dh~72Xy`bPM?cfw1(JV7J&BgKZ{17C4+Raw>NeYu5>zZ zsgw=HC6r3bblYS*KW*z>nj+ee^iO3IihigCs~mH-PQ`By$oD{0%6W}{SsfNn*477`(s_3Iw{_n#Wzp@m{wFl$RA=|c8gTgk|h zW{Df~ytma;13eFjFmrfaC5&CxjxZ~JuFYjS@>EOblhZC8SHS~< za*m;m$Rv{PSTEtP3JE#W4Scf}^=~Gr8K$!<(CN&nQk8IPvK*?_lN2e^o+P{ENm5so z0?(r@fLiW;D$uPM7bRL41C}UVs(!(Irh7Oog4DBDssDbeG<8RYAc8m$56m1}JG-C5 z!{Vo>r)_|9z+Zs-7~J{2ht9o%vYZbM#U+@6BdAOxS%%svHkF;a`%~L)e_we@7?ngk z7rFWkhSUAbeen%zhO%2FT3KEoGt>W^cMIA$vznsuJUb;9{AU1foUqg1YqU><^SJYPDiUU{7dr$^(wKIYidb*M8Ks-ED7%;t;wsypE@|xPvct-%cR(5Q(rsD@S{Em7b~0s5kOk+{tKyNI?IGy$Li2@`f>5u6{+u2Q{<^S%+xSopt3+q4e(x5q(`F|dVT}0ud)aQ}qN*pyQr37wL6C^^iJ#@Hmo%RV%AY%$sqNdE?nP?xsn zv4AINC%U57K^;(|cm1|_a{d-;P!s&~^1ar5e$5=C{byy-`@~=JajVTL{hDg0*H!&= zEWI*Lvo5Q+M#H^QoNo1X0xmp@(c!7aeDu?AG_R zeP#LS_x-Kow!cu^J^nZk*#~456-fMmW1j#NN?3l;oTXIhT!YmD&Bxzbe@DP=&T-vw z#P*upVe$zfg zDt-9tnuoey)TO>L+J~Lb_re(&HNVrdn^8Hz7IF=XCQDLE#=|(cYt!A~@oEIeAiZ^mzQln3DU#Z+TziL4_?@py))hG!D7!JvQ6!@n<;KqIRP6P$f;rt^}X`# zs$WE-*QCjE?+G!a%6%H7v;qtz?-dne$Uon_mE=FOWv#v4Hi_b0aC1I*K8UXJtsc@7 z0EuN0z~C{>`?C9SvqhBT>o7L3yrWywmFD_&z{8#$4`5bL-`#lx;YV3OJZ0n3yuWgm zw7Gt``ut69tjqbP*rv1XrGGwKll<010acpg#?CS2es|)@iV!b}UiA(WBnHUMA=&zM zMjRVTFyo?o72{A%P8h&179D5ZTju-Errr`M->5bg-c4%__r6%kF94%1j~+Mfs(`?@ zDAzZrt`R;{`Y%6Bky1#uu1U@-^vlVY2(^Xtw;g2vwg-Ugvy~}UA|dg}8Cle}rvoi4#996qY*x&XHN*I!~p_wLEct4f6k`BjBv+21+VgoybkSdJk!i%2###%93 znP9J%hPOD-;nva_xVbLrC{4!<`rJ%JC)_Ysl3Q4n%NS8pS8lF-##&$4tXePx8&{%p zMe{altFXJ{q6!OaH1v@C?__8_sLItRtS#+ao%_|(DuzPULp9n_C39(3%Idnh1fYP3 zd!ql45fk9-BV}xCYzRmlV*bIjtwkkL;3pTR?SEfUt=7J$(`2yA_6G+e`$3B1E&rf5 z8`4mCL<>7box{6w-+7CgrnBtdiTSPeJ zc980u!W7e*2gO3QiR`jJ2hLCP%3uSZ`_Q&C4Nm74_nPLWYMLSn!HeOZ5U%6dI-~Z(;idJLwwewKPKa@2@0mu0@HX z>V&@cGwlB=B;PQN|DG;XX*Pyc)^tP$BlEY-Za7Div`B^$;sT1PqE94s^DuEneQkmJ zP>#-V;8{)it7MEqLw*}kXurP5@!Wj^Dayzhm*@4jLuj!yuyx8zi&JfrV+xQ_a^le? z9IFkd18D*x>CAxyPpT!nDuFZa?)=|qQA6koQ+M~k>C0Nb*=nKCN|xf`#yG;)L4X9& z^{JT+_z^uli}1A&FbXFKjVw~D(zrR=dcSlh$T=!+R*PZMQdKP`=?91ljW&m!i17>T zP8&zK_rE8IdGS&wa$rQDc8bO0caB0P;sKfdK74Ak5p@V{_y>E28B?oGBygp<{H3!b zYKbt{T-yvME;guCsdgbV&w|p`*E%qH~GlQzPmW{ zS$A|3YHQOQ3xA(3TDLwJ^AaQnwXB;93q`*C2&vKSRQqRkX9w^VsGlF4!}Qw8%IiSN z!$cSbLq6t@ng`Af5mg91|3Jjf3N}fYLeeKI+B;!C{@%E-^}Q4WBXD-bx9f7$1Cl@{ zCK5+**LDU!X_mxDMiH&i!-~kDdDP1$psbBK3)i}KzpXQT<55^UW2s-*4t3P283k@D#;JA>yevTsjc8qc&%tl@%3LOyZrLoz1`*>&p)aK#gK(hsrDpH@{ZS>X8kgHmt{w4s`R#2q=F;+Pu`h6H`Uq~#wEkvPc>N^4ueOBpTAyO6IOlkLd$g$G3S zD-0xLG!j&06T@h9*l^_>p+*fyP#VoIEdH# zoo{Z&X+?t?r0PYw2o@?pvBM0+OOo|dmu59w194CLP1Bq_@|Pb^WX5BJ>r`{CXxOou<@6;JnyF{U4 zkj)ziqzQn$rK`{^{FE~nR8?uVeS5n3iUOd6X8`a}UEK_DZ*B);DT~;JHy%3v?`Qhp zoS~P%zH8X3$yo^cEg}&_4vbI}=Q@#QLT6pc@`l~|aLa>WZdk1TrzzNXI3h&hSa|30 z+9}?K$k@(0F*rMdNA9z7j%szwBxahr`w&5?Zc=ChI_4Q03K^CmIa%qge03BFW;4}M)#^jHX6j3=N@o0=m)Vv9VP$(vf*X%Xv5EN?eSE`sy4(5l1lEp^qt zd9l0qoObTne(XuhsTWV%eeF~40|=i7X&v$-48hh;5BSw_y>uO~u1T((J@wRr3}b5p zi#ebjw1{mSSm!uf#E2UcHX}I*cu?WR44>~)YeNdADSzZgSa^GPE-o#d5qh8J_kO-# zJ+Ezjv?%^u0}_Q#`qOqt}aH8 zWfhR($MUuu09Knc4tu>~F#@KBLT4eB5mG1C{@Uu4f096Ovwvx6sEzk`in+3QY zB$SaDHCmF_C6&t72iRIz7y!f4YuPaTZ;>mIQoREp&0oXMXk5d*ncc#1VtG7tAVlEV z5f0{crbi8BFn32&drd5d20maIHD65eFz7Kzk18x=Xs&^*X#^T5^9pYPbg;PK2N_t$ zk8DuBsls|kBR2`xX^s7yBPP$=BGgVly%~(yf;)=8iG+_T6brI?phrX&VRN}owkgJd z4gC@@+)eLgwSxAPdtyZM~FVIp{=Z}IG^{E zXnmvb3zH|c^1+u;s+?H2sowyvKxMJ#8^pV5h`a{TPTORV4KYXQn|l$ zR$WZ}?t=T!Ltagkhdc?uZ7iL8NoMUgo*GQYU0m<9c5l)SzduoMm?lW7oP2hFKLqE1~5c3QWlp*9m{2;4t0<$-mo5vDCqe;Yb zK>b2ax!8>!Fi5cpUWLq^1lm!z8~gSyCey`WSan|n@GcTX5UV?KVp4h8vyQDo5x!!N z=NanD=o&NDKJ0|q(vgoV2+v>=(PIT1UYL%(xl znbpErf^+fXC5y4w0VleyyJf_8N0TDo3^3Rp2vIM=+kcnIsS5~OHZUhW znhkVag1xnv`Ir>y(3bKFqn^-ktEoLF-$pVt3n<{<@3 zJ*p+OCVgwXfWH~G9TJj+6uV3)Qr(;wU0xPQRsU_%;Xh&At&7JN0}PqxJP?iz0edVO|JR)Z=2}aGAav zy@>!S^XU9%m#-or(=a}u9;(ykpcxtLfSPcQ0QeCIL%eI9yUI_5YcvNvR$SkCY~^7q zrM$Lcj%~d_*v%bm%*+jL7jQ{3^hpnWlpTz~5!AX%WD^x;pyydGlLeYqx;fW_JzZ*p zisW;uLj`^i|hMD=8}q~BJ{rY_f&8cVxE9=1_#Xu2)sG^WJ$8)(^D{*XeI$l zydQQyD=5SZ-hVA>$Ej=}b|WG9Qm;6SBa@gD4e0-RM!#MiOQGG$U^az|f3J!>SxYR? z+*udKAMt9`q`q%;az4U+x)Qq|=o5ppgmiN$yi**1^StAHui_$Ok&6DE4YvlCO6*l) ze$k)Yqil%^EvCTToD!>5mM$YN4J%alp)9x{*TiUfje(!d#{!UscacIZ zp0|5=hmMOYM|qMFPQlo2YHcdle;$fTfD1Zu80fO5r8ASt$jDH@sIb`3N{9pDpmq^k zW<~8Dqed7x7Qe;S^`e+oMLEJf(y(XG9!qMNq{# zbo7@UP*S_gIVMrrOLDHO=dN7uO@u24ge0cwX)NGj8YmPCrPP7|MD#es`mqnoc8BLu zd5(c04SFo`xuC1~(8=y1mn_R3&bj(*D-2DWTF@1{rumD$1C^9S|3Tv;{ zeRC_Pb`aoUvXo$W$ybST;*x=`V|1tzWnwrE@9Y)Ui5k#fO9c~FiU*9Dn#qJxJbHig zrfwiT=Bqz0(JVP!mZgHhNQHM#iiwN6`{htYWb8)pk_n8`U}9x-KEfxi-!wuin=yYl z@D3+CxjE3(yCmO6i6!4b_GJq@erFuB_$_Opw)+%%pk?&EO|1r88^E!6X1(2UM1+6gmgQ5z3`%X>a zA^EAalmY4rr49nxl=^ak(-4v{?8)b+p7LJMN(B&BsI=JnD9Axg+c>|>zF|TpO`;NM zMMWA6pv38*GlB*e;Gz+W-oU7$PKgN1U~92*psRxCL+T4Lh6tez8So)j8hk4=|9Y=5 zYF6J^fo(j^I2aL!@s&6@B3w0i8Ke^ProlJ(+*uvljKN~a2oeD*2!*YCj_fCmj}}nM zXVx!gs+`7{^bNfz9UZRy5GgiwvcW%`d?hrW;)-KT>fhcdH*y!=Ukl4m3|r~DSFZMkIYf=V)Hh3^>mSp2E!qQ&v}y+@)Q#) z?TF#cqrv+M*1#lS*C9IS#iHHmv^0U)jTd-!=$re=ab%<-46hU3KzA$_p#!qYesp%k zas{0!UXG`@X$iP-cP#D&sEa-6L(+GiXQuRgk7Wv32$B*>I5)tfOOv`9-@O=4`a|;z zXxReyfr>a(>Oj~V)4J!MzcsB*Q^RDrWSl^-*IC z2RZW6d#vbX73C}=XPGCA!#1{)OhCM;c9BKqc-k=?#qi9zGo!DK?EM#DKL7 zdT(RAFb9-PkxIYFZ)UI4S@Bd)2tNb z4y;RYKUBQ2V3(7Y@^AWJmP8hw$3n^Pnw^HV4rQ-gubs^nms$G{<0u%+E3hNtA$k-L+FX#_zR|x}JP#zXH0fjtCGf>+32t3Ar_G^ItP?FK2ceLxDdHz#5 zj(+*tV4jp*icv-%G{=95w}{XE@Z!w7493Je-GD7Xh|0sFuwSvyc6fFL1GepOcAVDN z8ow~ja;~J_WHd3S%Jzq75YRvTTOju}^~n$7m~E?s#qz;xiqPk=bITNnuJaljlm0Os z7K`RJ8Rsg-QY!g;UlN<7wHlqrwTFD)`~>2-Qn{xnm(6HA{H}KV4&a;zA12Y%%jRBA zThEJ}NAJ41Ea7ga%v!x-G1^ek!{5&5%qT?aJWwIhf5E5rKN_C0UXyk&fI+oFtCtp(Z2UcW3&mz`s6hy8INI<4>TzOA$ z9lfnjg>FJJf60e^Qx^8$Y2Z52>+7=aVcA{!8=Z)RDE?F?7;#+(CwiX7;=;uaB-(${ z>y80xm7ZI#o0JKX5XKEThjF|wSmm{~Q|+ylAZQSQ5ile8&e2`>vob2`>UTiH?pYg@ zh|D6d3VWbQ*;wKO8SU&(wm@>ye#+8$r8Ak&bO!*GE4>>C&tq%8r-%5d7fQj!M*Gb2 zOaX`Po50tlj`J`qNUs*Jw9Gyvt@6@?fuGt>36I)To$_bxsIdCP$04T&U3(t>QFX=x z42d71yn19G>@9)`n=~RVXKEBX_y(b+I>xp216ymHyF2~E-DHl*%Efr&;iVMMV%`EvcfW8K`bLbCY_0gS(2H7u5-{U)pNY_`EieC}GHfudrTU&+@j(xx zaTax8g(W%61X8XN3Gfer;e=k2K>1j*bnbQ-&mLP)ty+&x2wNH(iejQ?d{--Es{oWAXgb)wM=>soe{@74&j} zAvBK}3jickiL8U_g2A=~Z-+DFLFF2My1IPd*0Gb?uKz}IUnLfL*K^LIe>=cS+!i)y|K;Vp;fN8e2tbk9PE>uqX zf4=H_0y=5?bpuGjCeVgNrM0tL0+XM$;Ky2d3T&3`&fOxGkrIFS)`}W@KujZ;6=S#ZJ3>4{EiThC_t)IgPM^G zRny{?2u&2qxwoPGTj_a=r}3^T~$K%y`-wG8i02=dZq&H zlgxcQXNMe-1lX|lca_VOKizPT%Fi96n|tNE%%dM`!)LEgv57BKDtZ=_8N2v=G0(5 z4>qB({4Wy4Fxl+7y7}wOAgCa`BUQ*ubdMJ(jukcIpXkiFiqeI?9Z2F`ek>Q^Ly42n zk0Tad82+Zjf@;`#(Xt0fLL^BgQOvBYSpMT2W-R8hjiZ~@GBPs(AvM!MY!RSu*3wRI z4>X5KoNr2b>+xcLG$|oh{QQh(pCPeVD_6>pm()g|$h<>1zA;(dG?QLf5IyO<Y!3MjtcGyZ?pqM9&>Ga00{Un+xDRp-IYV|uY zC)ggfx?X86H(MeRmscrobp#H!8gmyuq-)E2x|9e67gkXg*&dUX=-M(cc{$O?zLw}` zYHimdr{Gn_d4&>ck?&IXNnn%^zj3EHl9Ed3*K{9R_4xe}i>TG4M%)Y1r zcb5i>U*=E_OKy>=cB02E&XPp&z5mg-L^fR*JFE-PewkfZFpXvc*p+m?$8{FRAq@4b zMNZ4vCGw;nke{A`+s&y$5dMpVPgkq1r$wVly^brUsn4HJBLaR;Knlx6T~#%`riPwW zpHpJ@%P^P6?>m)KkYg~SCs36U_4Qj?Y8+GF`7gv!NbD? z8SmoGkufTWg75(s?!k-DkS`mq)EdM<=dl#@9ys>1Vg%&EBK^fzKRvPGKB*imS~|4H zqw~hY2!?GP>IwgN3evLP@b^6j&{3LWjNmZ&co-x+=n>fWR}k-aE2C0jj>G12D#EJx z<;g2d!)VNBENJ<}R=F{PBT(v^?#Vw$v3djk=1c7WlN*#h3dx0Khk0$R+^QGiEn!0>mM3t8b2EQU+@j!l&Q(bmsx~#U={C0ozpUxK$ zDE;-#txDnn(3{G5A&48^mxHT6wTY3UPG{NBX5V6~#EOiZ?5oaVe@+>yQ2&=gs|&Ob zUW7ep6D9fp*N-9}Vz1-lDE|!n3#(`h z!)BuZ6o|Hd99yCMdm2B0F_tQV#3fqtJ+s7msiwHFuyBOM=Vo_ESBRofr^5Q$Jtq_^ ziX@@S2>Qkc(6H;hqnC6=OqVsg!L#q0Nw?PGtMxBv?qW`8(46vmgpb2m#ETWCu`5)0 z&u>!y=5ELnJgO%ht8{@iUJ70{$>4es_@cP}V4-kYC@n+L1!IK%1=dY$-vHIGfN-N3 zm-&4XC|4h07wX(~S1N>h=zjow&%gcqw5UuzeOPYW}YsFlR~Lga7y(HH+ue$i4{3T{udo5#!Vlj&Q;w>ghrAbw>61<;AB zKw@Ez&2AUF zLs7kdfgt)-yW6iEsrqE@35$`oe7D$I&oSkh5Z zl?dA(-@4@&&Nwj?j^(xawRFNOHtD;CQWJ8wYmrB9soWL-Zoxl#-_@tm%w@8vI+a2i zhluj_=t--H!UP4hZfOQKvuEaX&dZQ>UT$IYPAQ~FBDi#$?vo4wT&nHt6<%~pAM?u5 zAIwlZkr^yV!!-_FFwBwPHyc+vqn@d3hTK77RwgwtTVLz!E1X(UA)9+5pR0)dA8jWy z15X^r*6a1=fzmt>1I;Zg6pxxTP&#V?*en0d5BawTj^5|*M}V!OyLge~SSM*xi73do zqPdfMT6iq_uAR2MSns^bPD_JV9ZPLui2@lw1FFFFvbLjl64>TE*v#k33J%9K@6b6t4 zk$O)-CXjK{dEsit68yhDv8BZY;o)IILa1wKmEpl%JtPv!3*D5dtFTbOELcb?a}Ml zs=xP>e?EBn?Kpqe{cru3%{WZ)6k+=+ZApW|C$?&X(32<~6r7RY&KDS|jvtVzMjdD& z;+L!yS7>ecPZfQof_ll*T~*@kUUtgGL$1+6NPPDS9UUD2#`N{XC1Xj?`}^AJs?DYh z&{`coYH~h+EO7fLlin8%duBVqTKThz1e(}d~)6^9>?t= zRgg=+HwwWd+IwzO&dhh7o!+lrgL2Rr+TSy*G%nMSkw+z z{UI8c*s{oMdbF^w^otMv8tLC}q=p3`1DL2TvZADNsbzGgz=szjUUHRdN=<3?q zA#Dt>Kp8_&GsV#lZ+q+6W#oUp<0G0qDHcx?#C{)rJkXah zZN2U0tEMy057B-%H@AwzrbH%jXeIoftH#OsYMC5b)hQs@mES6vZ3RSu@hnr%t_&Wn zUz+L{J_8-je&K7gZsmGIZm6Pm#fO6FLm!q)GULSAJFFo|0F2;2^=3V%QSvOmmy^}? z^(sJQcD=)e$MUv_5iLe*D{fhmtRlf;o?R{j9c)p{I%%}%9~@C6l@u_;1Xsm+Ntiqm zGfkk)tA{veWqQ8Mi;4@*^72JlLUfF{~bin`tmX&i6jIDKbOC_Gk0#7q{hf>3!)g_^4A?%1roz*Qk_Y^Sk=qoc_(CAFzMgZPD9OOZz%a9S90pW; zGyGF=g_{bEeYf&4Quu(PVe-19Ydi#5`^?Tp-(x_^(9uYu(fWi2Imy-??^Ta9H+=_} z3-CwEK(sjbs3T3nPm?oYfw>oA7}4Cq0uw|lwvW0EE{J%qeCRunk-}dh=KN>Kv{;sB z$ksCbOhKh&-mm=6PwURNN$M@}@*YH$p6d*eZnxA3UgPTb@QMq@(rE&u z@ItWr34)_Xax6Sskhn8IFAoN@k1uIdL0MUui7N|ullZZ43Qfz@ zc-=nn2g-ZHF_lfrv5L|EyDd<-RH<^3poeoF{IlTy|F7G~PKm&y+0;=7vq*wiu>b#x zyyxQYe0mp=1#;;;i+KNk6_UShOWv@vg|c?>V*`PxVaI`!Cc3PEM01NXHDIVd2SB_W zIPo!r;6J8lT&Wm`c<=`X`QW(mktD=W1RLZWR#u#|>No1Y{{QDjJo~xW@y(t7yXL%u z;XQi7PGdgD|9x*Dozn5_UG6}=-v2(GLM(u`)Z;0wQ^Yy)vWwm^u;ZW-1UA=UMZcDz zd>D(^NB!UXfDjwgR?Y6-s2Ad7_-_;p0z&OJGhWUK=l*aZi?_?hajIIfBiIA~eSBGT zB8>?=z=eAf`oU9x9pQB%A3lJ_yVu_yiTgM&#|sGEdNZ}%5h7XIdGL`&DEjYsWBDF5 zG+S(NNDAWkfwXo-jri|kMMz+~A=`yd)p7cP@AUgC5b4MAzgWD_ruL{<-VhYx|C?d> zu`!~oxOD3&KxT0aXek2PW-tW*brd0rz&h13B7E1@&IGyb+7r5a$J*7*nPFP5~o}0s*AhO_*_j zYb=LO1BlY0b)aX|4A4xzz4HIWb2?iEG<9mvEjH7)3B)*)I)UeC0|DfMgaDRX;AJMR z2q^pmNRkvuLOhV)`Mug~jVS2s+z2#(k&r+^sM4M-RnvtSYC12u+aEW*HM>0O%PL&l+;F!Z|LtK-O-;WmQfRwn zxoshsXaZE{y3AsrJ%D{zOJARX#3+V|J!#aS8qmtUuxEDM71izbaxLyW#y0)mSRkFq z4<{T09NF4{70ANgKGlVr4iI3XfB`eEW;=0N1u1E7U%9%xWY?_a1XXQxvu#9v0{CDz z7nfxVR%tS{Us~2%o<{^ZelM2qbGaWtsLn0^71-c_@hqMOboqn;cV6f9_n-F`Sp^Ni zsT$R%{TMRR!w%y&0Hz)q0B~-4g;oq#gcS5V3`jc+MHQd3b<)kM6&QM3^8! z;KqO!&bgTx!+u6qK(qEu2oOzuUFf;G%G1q09&1%3X7)4kEoymC1|YPnP5@$FiFyFO ze}s2R**F?DNN~&V)fRy2rh)UqzY_!|gPzXGCAT=LUC6(sfb{o21$7|t;(3^EUew&Y z(DimZ4)oY~FKYERHqLbd*(!6N6B+#%AcNp*e?5CgcGVF);s1dp8gbr2c_Z%&>fibp zFISq|d-sP&o_{Cg%Vy-rUVa5s1@23aJupGc*H%`_0ac(XjrxLFUAJhob(sI&VmuJK z{!pP216tGmueWOrYT{hOk=6lPJY1_e2!cVOVr>!xD?u(w3?L9%0pozuLI5E#axtI~ zsiIaxQBqK9QNR+k2@ByOgj&=|V;eLYIB@Vg|M;Z;!kZ`~ zgT#b{zQyj)5K)LgZUGdXy_<0PTqyiY9{<^}9v+<5bSLk#vun#x2r{|_l+4rioseb> zh>TndMUF2i>bq*su?pZ|dNBRg7Oh#>-B7<785`We}I+#Dq zRkc!m7O|D9&S0md5uw`@f*{TV2SF~uvH@lMZX$~n_x%1>`bB{%J1o~ zx+~Dl4me_b`V_66Bqo1ef^6(gu&bt9482;ex-ki@5Ni^3C4{Y5cl`J4Z)1E zWe=Vi*J_D5+f}pNF}g*{+)O5a*7ibJFG?3^M$23yF;!v&fi9@kQTHNH0nts$Lzb6$ z322F@ZQ%cYEA_l~F#XBOL47zlojyP;@S2~L5B&P=mf1_Y{6;z*{B0HjaF?uex_ec< zy%t5?{&V9Y79JY9P$q#Cu;Fzey1ypl%66LB&6|G+5Tvm@IMj48%0t4bNI#*zcJ+kDOUx1>h*GJXk$0RJ z+A7b^9Mo4b8H^b4({DjrKx~Jum;$N>-XmSs)s>A*42*qAYg95e*Z1s;eEXt}$D`o# zK@EvkzCtORuN6vV=K1i^PFxk^7?Y{Bg--kPM|^xCyTt-f;Y%c5%Dz4gr}R+cllWyT z0Pg=gp)>LrFvJlq`=PF`>zZrg-WzbKBM!!dr};cs(>(oXxJEMbibku6(w#VTNR8y> zNejV~%vgW~6F`$?NdM{t*oJASfpV3U#;7VQTYn4gF#|rH)Z7ow_1y1<-a>5FKt@6!`ek^LdIF*HG^`uwIY~RdHHaUKPZ z*gFJeM>)@Q>qcL{M)lC9qki6Cw7`qUMx(|9ns1r*e1Q~I|#g?r+(`-Ts6>4q^Mb*`cicmoWa|^7IH{kRg38?Zp5SSYF zC$LV-YWM8hU;MlZ2D^PtJt3_5!?-FJkA96MpDJJl2Id+B6%&XT$ozd|t53tlFPx}G z*~yinM%kZ#reAy%L!!5upuIISpq0^a0H1+dZhis9?I@ydvw%K}A3zsjf+B*&V%@B( z`%Dm2#J6;!hP9yX5>%E?WqqDC*b5)!vTO@!@G;WB^bPg(q27~arYVpEp2+=tZkLXF z>>qwa>JpSuRQ%kJRluHpUe~tHp@gcP*ALckM(WN>i{0f0%FfR3ftaMuQ0THivB4a% zD@wMf@CO)`q6UeiG&1R4xL(P`gYprwuR!tLUslI@+~Ipu3Qxxg*jzE9p0_%6CBk@c zI|W*a(oHDuI`G}xdJv=ua#|CzKzs#}0wOvf2tHqKwQK-vGY6Z?1~@lyJxKW7dFdWV zZoT&}HSXaH<9Yr2cu20?l#G=l;LS-lrM7*4JBf>0qNkj$&lqyM%Q9W|cp4ze_xcfp zHJdf7L0)mt2j2)W?x4!crErc~g&;Ah_J( z<&czPb^J&{|CWHGRrqC9`hib?s@;$s*LW+Qv|es*j$JB~z8 zje`@$h2XMv3jvO8VFV({?Guu#>t+IhL?Fz1d?Eag0j#9h#Q2Q=J>cv3J}VgT;omc` i6IpRb+0m@z|1xI#@+8*sOU*C_6R?{~uHN;<>3;()$eB+7 literal 0 HcmV?d00001 diff --git a/0.7/assets/_mkdocstrings.css b/0.7/assets/_mkdocstrings.css new file mode 100644 index 00000000..049a254b --- /dev/null +++ b/0.7/assets/_mkdocstrings.css @@ -0,0 +1,64 @@ + +/* Avoid breaking parameter names, etc. in table cells. */ +.doc-contents td code { + word-break: normal !important; +} + +/* No line break before first paragraph of descriptions. */ +.doc-md-description, +.doc-md-description>p:first-child { + display: inline; +} + +/* Max width for docstring sections tables. */ +.doc .md-typeset__table, +.doc .md-typeset__table table { + display: table !important; + width: 100%; +} + +.doc .md-typeset__table tr { + display: table-row; +} + +/* Defaults in Spacy table style. */ +.doc-param-default { + float: right; +} + +/* Keep headings consistent. */ +h1.doc-heading, +h2.doc-heading, +h3.doc-heading, +h4.doc-heading, +h5.doc-heading, +h6.doc-heading { + font-weight: 400; + line-height: 1.5; + color: inherit; + text-transform: none; +} + +h1.doc-heading { + font-size: 1.6rem; +} + +h2.doc-heading { + font-size: 1.2rem; +} + +h3.doc-heading { + font-size: 1.15rem; +} + +h4.doc-heading { + font-size: 1.10rem; +} + +h5.doc-heading { + font-size: 1.05rem; +} + +h6.doc-heading { + font-size: 1rem; +} \ No newline at end of file diff --git a/0.7/assets/fabric_sdk_token.png b/0.7/assets/fabric_sdk_token.png new file mode 100644 index 0000000000000000000000000000000000000000..e194fac833009458208bcebaaa3ed6723018a92b GIT binary patch literal 90025 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

`z-uX zknRkOQ@opsHao4VB=)hHSJxwL^d_qI@sV*REsdRSCf`k+Z=LG+A&}Ut} z7BC^aR_>15;<0Z-9r^3zzrnON4G(Vs^9X<6y@PD8h5nHCiNvU;$pHR8hiv-j7qL7F zF{~!#>%+YV(2>epq*r%#HpMAiz|}LWzfV}z#3_k^+MHxx@&6y%-OV;Xrp zv?-#cVO&unul~)T0B8&VGni*#gO%sYa>WWrv^DV@#C_#`Cix6NVql;fp89Cy7V?!; z{7Kr;a$gGDs-))503GJ23C5@|3EI|ZYScJD4r{!IR+NC_kM}Fy z9#U96OIcI|dBp8`zxz57<8*)^6l2yL44ONy;YyINGW%4 zB439t5MEn3HQ(KpTW}N|ce$Z>9bssHq1L%Q*;QyfY}waN|GC7Dg4!5#0T9jh95Kbh%yok9(& z)Cg$f04>8$9K$qOJ0%hR7xh!C!<7@*iOu?f$BVe)U74V+L8$j)QF_#1>}4IH6;Yrn zBm#z>g}_h{KnL=r7SzMYX3VS%{_*4OK|~w`U9zYikYRmz16n^}YQK#<|A)?lo%MP2 zFf}{e)P}Z$%JjZaX&u;oduN_2SNVOzkGBS1hc{kZMWU3}3KLVD)Xy7hzHeB<$^$HP zb+xJUc15JEPqw)x*~FmOD+8iQAej0@*g;j<$DS|bh#arV_ju*Jat$H796M(c9(vT00x31P=WqKQnWTvncN^om1Y-8Wc;h;-{9SAfFU(g3(w+$oU$gp2Ogn{ zCRcK{9SI#lG4V0$n;RY_JXK*dA!(q5k=sXLPGI3dgG#-E5cSL`^6PKfUEA=5xI+ky zuZ{~EKNcTnNF@LgIg>f;-TP;yzVMsm@jB!SY0F>q+dyR*%s~&8+RGAAa%y(MhHc8mTl}ur~}2o)IUthfkv{@f;r` zhL7oc?lH}rZ2xS;5+_KrMBM|-=24R-hE_VVlbR89Z(-6-^2 zjE~HS8!7?vgJJ#Og*`h?EZlvNHD7d{;J8OM=y-pqCaojl)R=Cf>IK9Ma&YLDkgwyA zh;PJ)YiZJ%#yCLD@)M5*%R)*0zTDf+w-$llL=tyu!;jqJ;=oeMX{K9$cEfX*4_5GC zIljZSaeHQw_ZOTsHrjr6I4gy71=xYUJA zEnBCmX$^uB{Fi;3ZR7)?d%SE>m0-%%>S37MS)tn8k}6e=dPho4DnSVeJX^l}u!w(8e-QK8=2n$s@^DZ`WIvMbx>+0h-aU%>_Uw z9*k9Iz6W#fi@gMMe=l|R6};(ttd^4k>on)=y0oD75pWb1Q09QyriAUy z1320VAjO2isdt*xuBj2XTs{LWOn%MOCCRrh;ukbCdTc_xv+NCCN0BL_yRAsSBMZOK zGsDxVqt6!-r(z}&8+uiOwtZxg6;z(FzMt0v(yjb%9E2HTADd1GXwRvK4zKoPH>duC z|8vvpz_vQ3rK+W-8LJQZG7F&Sit-184ZTgu8?~Fu{%DKJ%RP^oA-*V}*X-={e7XSm zg^pE9#23Fly-$InBHrv&X1YyVQ$x^DCu3?2tcg0=Gkz((sp4TmQPHVJup9D*S_GUbl7iAme-l^7 z)@12=mrcD^9<~rY2@KBZRv%49C$XwQ%Mu9@t9ugYpb_}h*%a3He74%vt<}A<9g!N= zdW+)^0~a1HMW;#A3cORg0EmG!)T-M2YBReE9n-N@HC4QRieTg49`Bl2V4-<*?D;W~ zl=#*00DmGX&Qv^)lxwB98`!a8E()&jQVGJz!g94S*}}@V9lr?Yu?ebKTL$0MzBQ}Q zEv%CGa1}K1hg4dpcTC$Aw7D-fRwWk$Mjn^`TzmV2jgQsfy`Q=#e*_LNqm`#w`IS_L(@LUeplGUpL`Gs8gMAe;*Q=sCA*Jxu5d8i?60h9UFy zHXy{x6&W|!PvR@`{&6dur|>mK>P2}sXkIoOY7p8h)Ol~LPwPBVJC}ED>&FKd>cBNg zM)l5v&u$0rDCL=I&{LJB=i`~Vz-~`VYp<`w_9j#`bTDQSOZ0M zE8`b!>`;IJkX}TJY)yQmO;&$z8w6Q%)0Kgjf(4y!ldIP<-i6+Feg=>)GS6O-(){xQ z2#zl&yjIPUTm7ynMGg`vab2rj4b1TQ(A-dGa6g_pSbQ9wvMtq@%Pgh=SuEAVUOdnP zq6O|#<$wdrvCW=y*$Ta>0&Y8Qc0+}}{4qKM7>FM}5t5xz0>^It(4FXYJ{-Vrse5VE zO5f{L%#U=~)Zt{+rUm~zroB;GI}&JYZ$G55PF&C&U2M-o*RzGR?{t;%V7K$`?V$ma z{#GDDqx#h6g>XPd`c1FO1S)f&;xCHQSkLXf(6c?NJS)e~bevz}Fan_BLHjJUzbd0*8FO+5PQadK~6Gghv>NTFM~u?cQi z#Lq8NY}P@cov*et{EcD#iH1o>FE8Le-Wd9x5v7rzQqLxH%2+!Ia^(rfcS}<961~Dwdw;ZovubYS`TIEj0gJz zw$Rer#-#}x1t%S!&6^8S;9RkOrkHbQ=e8G{V1Fwm=T?OJapr7h&J%z8L^Z`TFL?3|!mW9dY80Bn@3pbK>HAtORVx!)(@J zhN(*r*DkKAR@&>e#;)4bM|^1#)E^p7fY8tdLp&dQpC>gjH}kx@Z9L}Xj-F%nmwgL+ z3WL1v$skat6)+2EG13Wbk(-p~7tn}Nr?J)ruYp^F4C@SZ4~@@ig$j(>q3L-~s*ClH zpXaEr`DJ}pk!7rSBeucXs?zP75h|?%=-a>7dpp8qk1TxLKVH(JxZ&Z0I#C-RjKLMT zSI=)dzQ%52OQ3Ulk8Filgk@`3a8|%~0WABcpke}^A)t}~u-q|sYQfv_30l~$(gLMA zl3}8}I*gshGx#u!BCy@w0M3br#w5oM-dWyZT9*WYA%LCEw0#+J1F)1|f2K{I9}jkq zi*J9sMYra=M8*>aIi=nW|Yfd(`sP=Z)28EDfuIX3_TDgPaoO5@B=vfOWOxkw<U+l}gmKG1S6b%;UMHeNamk;9|m0zUtU=VQIowDle2pL-M6REXB@NJF6hFQoq&u z-bg4eoT>cC)}9|EmLT?Atb>Gt7!kbs)QqgAs)f(hL#^3K*IxGl9%LEskfOY|!zj;p zWBN7*EJVygj>)lNZ&?}Pjvz% zCYOBDx7u~vlimqvsxf;2N;AF0x0u{FZ@R6)3(-0c+AZmae$U`%!@~&H=gy`defXQ- zytFw74k3EYNPq5Eamaxt&m+e1@Tf02ZQKc|~~x^avRKL{LfuwHNk&#ytW2(*OJOkYILaF28g6`yL*J zH?@XXKLE}spm_W}MgL;q(%+8W^R?J_4S5a{nt5x+Yz{0La}A!$c-tO)?u7On5UtS7 zCjL(zsXV;|1)JxL`0w?1;-9Qf9(K^$GrM8C|8MnyyyZ zj{b{&OXrf)*}*Mohj`=WE1BK(>*;mH*LifcpQvxKE9&LzWBib*_@*7{SHaMvs#mSl zpOU_2^FOIYxtI_qw48KJN-^y8yc#bTGLob);hr)ZIJ!k^{$s+RSdmDnM%3&nKOOSr z!j&9kVoRMM5HK2RUp^47odYaf)WjOd(1E%*KNV~#IjK;mF{BrzwwHHW7plJ3)-%;e zRPBU&f`d_ZV}%;vi1CMwK8SyL&2WWK2q>q&r(~pcuc($KgDvaC8nE91tlp2JZyNlq zoTe5DsTF`j&8#MS7sKwuF|pqE>P%j2zDMpSWi~xAjN(q?L(!uP&XK$GXzw^|Er%oF_S8>42M(+dbyGaqOutVw%YrK#{=)KO;UL6tM!#ij zL#RH5P^$7>$`C2QnwA%YC^y>cEJ{ne)MvtZy6rY8gO907VPadN#?8-!;GWZwU&mJi zt9I>YBgPt;N0vJvi{6{-;43y4=_%2c)>*c)> zPN@~C zsA>$RYQ`e7G${eg8WWktMMHNuw5Z12@bG&6Lll9{kBhH|+_>#sfc zk%X#q#(%33*pUvKPue@RNocD)Pt4tWb*yQo36LiTkF|K&H*7N;s)4W&-i_?Z!a3|YBCq}26T zVMb;pG32SMJ1p~p@3|V+vqn>f-woRc->V!otT9hAFb0m9B@x4vrDmS=a{^@@;RBa$ zO7gHllFGW71ecFqmGvT_NY|;eiHSz*kcPqHyT!z^y0XQ=goI=sY#^j%)|V*2S>{K< ze27TEr84Uotz(8$>tWw_Qf~Ec+sfuJk=N^jmfcDhJ^Kvs{eFT@uA>3rX0R9YISCFS z(rmP2M)pUMZkVsSm6$po+Uxry0e!_2K;|n$J?=?~WJegn*ZfA(@c-2ue^YY83FJiLK zT3%j$+we?>EqDta`>7NvV}6|KdxNfatoAhhB8`+R6&L;aq!>?5k&qb+-(pL;%un1KZ(te*m#1gadCk?hkf&v-%@%8fMfG@poZXS6ePw%MvCL+ z|CDjVj3y+*gnA#tXVoJ3j4Oxc<8t!856rYG`P4i_CNMuPVI6e3HxSo#Sx|m28o( z&G&2nWb`^yv6Wx3wMDF@W%l*b@>jZZFZm%@1VZ9mNAq#7a7cK7s@E`*9c zwsyCsW6R+!Ysb|(p?M`n`8#?O&L^n(dWiJxf+zj0KKL6=H8nMo%y}|u3XUPJc9#*^ zC!aV3>^Lp63SHRxaWMz}VaX$)=x|jj``ei4Ztis44ssAJ=s6+lQ&jCR0P#T~3} zVBNDdoAiVm@2HrWAKhfYkp*ID7d>?}AH#_*tM%klOyMkrL|B~8oq_j51w(|#W~#<} zIUQ97L|cXm*e5D-dw_83f!fLySyc_I+R5I7Vc8mokmRYq%=!nOuQ7UtXR+LxrigO| z*WuiDa^B@=^4!^>7UpIXH3hGEQ~Rm2*9-7ryn8jmg07WN!hxAhXNV%^q(XJz}sg8MMi%PJVQ(~<@thf+N~ zFAIc5dy!GM^A!ns+Ugitb{LI5&$*22^i*HrC43fnTxU<*WMc>CHnD-Kqi<{HaH544DP#V^k zFkg6yc=@<0wAIoZ4^pLEw%F-u#MYmraolK!FM~t$CeM`~R-}*Sb0rJPWYy34tNa^q zH0|rM+f@6HrqGLP8VKVmankN4R{m3uIkEaD4bk7eWT|dUhDzig)UY|(y9bJVu*=kn1yr&H1GLdZ&Mmv~f z3kDe^YSrcO4F?7ksG;vMK!p*9vE17DdGww`RNSx^m0j(O@oTZ3RSxKxpHWH>;Jjvl_*3aKd7585O7H7}>ajbTFXj)3MdhF+Zgl5=L$lb@?C}h8 zf#BOj!w=5Fh`sFp=TOPQ7-Z;IZU4asQ(PUcDNqD%(Mm`1*nM9v|ECAGZZOKzOW{9q zo7l06hhy3D#Qe2HrY!#Rs$MAhQT@&L5GV#8#|!PtLYMimiYzL-qm)+86YOD7%|}cf zrM3Sqx*R^C@ubcayZ>GNG9dmr?tV1y6k~jxxyS$OOKUs5hIISCO)w4_2XVy{KmE1r zHp{Q?Q5o24g#P2a2z?UCoo4_2G$Rm4mGh=0m8SSk;O1NZ%Q&)4N4A5RKg_fLORuXk z26!HlH-B(u$^!QvH+wouE?ti!w;8~xb(u;2tTB>yM6iXeN|nE-VJ`mX_|b_Yd(;pg zi(Vg_j~2St;~eu3pDNlmhLX%D&-?FQ%p%vcvbgnR2vnkjoJj(0LpsaR|JVewGI2kF zij6)|Jiq65nn7jvE2Wj~_lU<1`;3LXW65^2_m>C{)Nf~lp-jmY)Fb&d<@;M|#$xmS zhjoP^N1+SU!TPe^ z0XJzAE->2n84<1Q`TJXy4Q$o))3fDyA>*)Q&Goslum)So&+RXSKe}1*Y*v_&E|-C@ zq6d!DUxaGd{(kvf`l^=&%th}WIav&H1hs8tVgz^Ul(BPU9P{BJ$zoy~2X*J!ob1$4Xf!2))%BlW zsH~=v)FQzs05?f%=PP-9BRRKUGo5$Gf{UX zkU@szC{eyQlCgW3Kr{1?qSiXvOzm9wrq|P|F`Yb_JMVZY->@K=YdA<%Wn?ay>{*D^ zlPK$HBK5}h!X~>umTdoIMC{BrIp$#IL|=-@W7bHllJI=)3_&0D$2WvHk2-V z1)0lfqxoVf@t|hQlU5C9It@qL7$+_h${Nw<0G6T@G{VYA2 z2hCtTnnzq8ZF4)|&fv}ib6mzp<~2n~aA{LSg5~UtYVx(Lu842UIv$PY(y$v@Ku;4| zcSH_<=Paq(95IhAyM*5>iNwAuXiQa5h(mfXHV)MLsn`ECa$sTa(%!Aj5|{kOfv1Ts zcd3)iP%jw&jw9u-d}fhGpdP$Zd_F{Ebe-_Y&qa4*ZCVZrD}#A#A-X2s=_TR`p?7JM zo?D15&U)%djbd2QBUm)HQByBvKTOAGWxQ=mw6@#~Uu}JOW!uI!EUmlwtqoaoui|3W z#^N;8%cd_tA|>JR9T|h@&nkcLDG2n=OHY$RUp)S9!2l(C<_1aIS}q8h9De(k+IXU# z2WB-M_d+KG>4I^ts>fp^u>B$VCu~;}ovd*G8*fmzM0m-GPK5_}xwSj4%G0;?k7SlS zXpS4L$0R-~p?!Y{(@<2h)e!qG?kyiZ>~Iej>$OG@tlx!oO;@y=M5l45d*WE5->ron zerL;LGM|47x8$XK+-o8%|Iv?WsHcVpl0D7i39{P>${5gsQqO(3jFdc4muHpVbRW^D zbgrTvsC>zRTyQObu3w{`o`T4l!uRW4x}cCds~@X3@e6a2VKS_i$$e!Yda~f;*$qoBmO5T zzgea{Dj)jiQRAN-lkWxhPXHmX#Pl8q9ke-`R}|e#*A{VJ&E?BLn?x@B8sg2iy%bz+ z+Mo!h^l7@kXp6r-@7Z5{IG+VlRN?iICH|Ad_*3BrYN6JX#R8AVvapW^6*3bO<-GYZ z`BHK5CHQKrl|PrA4L!b&wvP8p^nLbxEonGdYb1WB%U3|)F|tCjocPLR(m%XqO=49q zd)oOu%Bg}}1!7$c!qUrZYLDFrf^kCdt1k#KR%g`C%E&eMnq2!>UrLL znf##(O(N#RVE^Y_G`AhQq03XmaM5>@-vs1G`-|xI+unP8G=3&5+2_~xPEYrqAp3ce)=$66Km%UDyeXZ2honrOZrDY4xkUL zZVp5ChA?eY{Wa-|Nvb5tEd7yxvE*0BHzdo` zvY(BoXH`Qo%|N(W%Nvfxut0b;9;m&jgKl$a5!7ouE*kQ^E~k`SbRw;t7)~5cIOx)S z3bqrR(7GE$cExWo*eR5qGNu-oGdruXaIFutLw{<*eT1!V9xuBZQxf0Xt3Q0!GOkLyJZ6l1k@d zr;7G#!*(ym`>G;AT-CUiZsJn=XbSNVcH7&yU)*0m^_kt^+sa~r;K8PA zEJPan+m^L}N9#q}q5LEJ)9kOnnyjF$j-mTf4}6fa9{5j; zh;DJ2z+-iZ!Of1P5A3M~*G=jDY%dPt4$q`OgjbdV;_yae)hOv13UMD!7`Niz64fG|b_myIml+R-6dc@Z45C zb;_mi)S}+Cl)vX03`F72Nq?y*%cy))tZe-Kjm(1#TDc<)i?crg<;jSbCJpLFZs4vR z!41X%D=DV^m;-tv9v$!VPJEhgPO|^SPo z_+9tQ-kFS#f3STQ+cxjTZLjI!;9-z69mug?ViqF~VP6(N7%FbN5c_(c7-c%$qZ7za z0^K%E_(3f*_BWJrmCPO0VzgxTc#T)jf8{TfvmI_?(z zOwpu9?}*ih#Trx8Sig)1|H<|cpbW{K`pePqe6BF<#6J3^xlVChZ(YY>lD|U-fE)_ryn3 zHZux?lpsLoDK<}+uULDbSK9MQH%rd+H$>^TI+lWp`{S^&Xl$To(=k5UXriQ8HB3l3 zsNiFSH9fQhA()2j%! zXfExv7SsIV`nZymg}d@{Ire`K7>-%fY^c-RmDn#M$+)qB=vU4GNg}<@DiVu|;SU7W zC9fij?T45i@xuZvY8yTiELtaHtR?O+J{@h1jGP4qNhjEAbcmPbkLMUgPG5y7mUwG` zBm#ie`MU16-o_gbe4cUq18bIl`NQSk&s2B|nXDX3H<7wMsP-wB+LqQ_`8@b->i;nb zFGY?6vaT)Iya@l!SS+_Lrnqh4>XqZLn33e!z%%5&tBY6G+)DQByW_*j1-a9`ejd3L z0Q=rLD=+a)uLNhbGbO3C?W?1b6|w(N=TBE~&GfCBxlmMWw@`Rt>Z$cF{;GezD))^J zMud8C@g!E+l@W1@>06D4+8QHL*2`7xJ1%WtM1Rb<+^c?tm(U5b8PhQ9lEjxLUqU)8 zSrZ$pR)MlXz&xv!*K@k}#zI_bJMurE`8qVj);GfL|ML!OyMR zrSziiLE(j$36DLT=Ab1BRaQK>!mrRYFOmAno>9d>!cr#+og+gX>aP^9b=Voc=~Ok~ zXD?`HH6i=ZQ@l#t3RAV-ReGs)xw~RDzyU5@5;@WAQIg$6>|Y}-CCIpXH{0_y1_sX6 zmD;(@uDN;cTyF(sJR<_;&8cxvOaJDRKj^I}i~p-7MUNNQXH7qqxxlpx0S+gK?e0Q` z)Js09fo3F`mbF>5H@u}X0^Z^sS*qfT3RZ2sfXtq-@m(OkwN%>O^` z-aH)2{r?{yIwC?&q-;|;Cq-znWnYRmlzkr(hA~w3VbFq#p-xF@&vvUZZ+{j`MzhKA+z|zu)z}zV~&VI@jGj_x;*m&*$Uucs^g`4g4s54ei@y zmuS}RIY)lobgLPEyfhh{f4l5Mq%~F)CHO2GBO96*7N|ZqMPc8Pd;1L%P3VKcV?`zb zwseyK(x#EmC~CnsIB{5AfhUoiJ_z?2(}INVeZw5X0DLa}H<;++B?vd=il8{~!VV>xlUveAQ78S%6+++5sHl`(4gVAZk9GyEyQ>>VCf9L2y zVvJY9e@!;u1&1)+RO7T%WARsGCk9GR{HY*g*mI$D1d1k^2xm2#B-*Ip^M{X^W^|pZ zu`!>>%Nc`3VqM7S#5MVbc4HqSr(j!~Mh(LreJ}_cJYjw!W=WEldh!s(2Ezu~c-SP= z*oWLS`@9N|mtCZE$F3vC9Q9)}W8~EozL>2+q=)&ThNwH$D-72mHg*1^WU4T zgKnB<=9$CLt%8boIoU*O)?2AF0jfnZf@iJwlO&(6jH?$IAg#+1PMJ!WPUur=+PVvA z%J`&SLaAG`Y(|q2v8QLz$B2AudBAdx+^x2{OeT!Gm+I%+N|h<&?QAVcw^WwB#upY| zH<6QQdNugiEL2uecZJ#VsACSXjj)chlFqo?b0Idsn1(&=GmDSLPL}#)DQ^~chqR^N zaiJwdXAB(-+8Ju}KUXoFb+UWW*P?vpLBZ1IxM}Dh_ke-lR4mbNb}8pct}X3ysUbzB zA=>Hs^}2iCwLqXxFll#+fOf9+YLI-Gwju}>`Ez4qvN>qsUcu;NJuxm7zvv@++O&hp z^5^sOF5s%pl% zd+cQ&_B|fyiI>k-^M`-<-X15$8&z8MHM+8PYAQd|j8?*#GJZ)G$-%2pbjzrKFXleZ z{#osulNJ6U3Sals_xTRE zKDH3u7=5A3eGOtoMvEGXXtp01u6YdL5zqdI-6tn-#l1bdIPZom=fD)>0tGa=xMY^F z!+s>R=Q7;s<`iGVA@zd`sTU-#EcgVe!M>F(I0Zv}K;wuRFOT`)2cdDEjh=CzJtvcC z^paDi?PZ;A&~gPU3RuFB88hQEYb#`&K60#?j)F}Pzqg}RvWjfbHTe8I->B6QaARz| zg1+{>`_@cAHWJ(h;{)XH$G6O-8Pw>L9J7O~+Txf~WQ}4(rHuO0&$~diGk*J@E4_aQ zoUO&I(WYRYwwGYNUe#RF6{PM(=ywQgC1ZBLUDHy~kh~lI3$11My4u&i-t*N{aCgVa zua0`&JMGe%pFVd##_Lm}pQ#f_qajwRwmN!AqJ*6BTh`cPJu`#n#pS@@dYt<5)!1+d zJ~zAUTD{KRbaDa2D#j{9c|gxbkEJ0kCVw{(SFL=k?|vP;OZzXSvkTJ&pA(VEu;e6;-`KqPZ|Nbj<{A-3z z3U9)$WR?`c|6#@j@@dWuhuCXRZB(CAG@-f0nX#1A+H=7t6GBGrg_?tXTXiyWQX#o9 zE#C2XXi~_zvCbKNo|MMg>C@_#S?G3Egzut7l#(~DVqqcEW5(aP&8_4ORS-=PZk-HX z^5$gw05D{POc`g2z;O+FOK}ehx%x3pN@{G`bV}9E*~d(E{)$u7MB-*^MMEnGIhRW} z=*H)^3NtE_vVUL~qN211fqXOE_RJXhdd1X5W!b%YD0wZwrcsuLtgh|N${|K)A?b)q zJ+6)2zF9@eJdWwqS%?7aT_R9sL;-qr4$kmc^zkJ60M&qsH}Dw!-a8__OF)4+0G(P(3t zt!kI)AHO(uy^2Ts=_u;BQVwu9FHz9CDCY7(a=RlZfJ9JgMuA6HwIroj2V0nmGt z!?fC8ayRDuK0phlX369p=fMInRX~oo!ue?ImLpj6*9vXVI1Jyc+*~uJ*`XNU-2g7C zbg*|x^z%S_HE(V0go&k0sF^6+$Cp-h>7xb56pUC(t|Z36hCbU6RuopfMQ**W<%O9| zKG-RZLwY2aublHhUmynLUPU&!pRVWe+`g>DCpuKK?Yj%b;2rN$e)C_!vDLr2d6lw6e~N%j)HXbQo?K zXz!l%!Zt<)M`nwKd-ct2eN5K~WqA4jwrvn4i)k@=oMeKAhL)5kJM;@`w6jj=s=N3z zFP5c`9~&+lx)H_JifbQ38YJ8i=XoM;+xVBtx1{cTaJm^QJbpmjce-fYG*P)?SBikh zFujW z+w&jx#b>)yWMRWEqgFZ#FJnsR~@hNM2NCHTVvN4R_~B^ zMmO8htRfNh=MS2}G8<5pAi3Jm9h)(a4BSA$8nH18&AKFo6@W~8*2U2rzc*8!u2bmL z=QiabN!jE$uQdwtj11N~y6jVIV>{aVq|dVBOp{s`ZsvU6D)4CIP)o}*O{(AniL=G| zs&)pkf#Bbb*KlgV)!4CKKyW&wZ3u$GgpI+-WO%+f{sAnPt;N?^gVZ9M zAJ3U$I=R@X&mr%2CX6>F3={lppJt|`)ScyIcM(qo9#2Sqs(qMHQ1+#y;(O6=u{ zQ~j)P{kYUC{Uv9T1T4r$?rA$!yh!0qF>OpIUJ)gQp1@yC?N}0B2xb9iv)ZGs9+v9} zk)F}@!i!w}Ml6#}@F7@^j6^{5+s=@pZY)l;njWO*`&H*ib}cgIp_@ALrV$<%Zyg&S zB**>P9iR`@YO&5vF8N6)zL7qlDHTN6Xg`y@F^=7EegX$zVS12R`oTVJ2dHN9Vtq{x z>!g2R<@;m_;HhZg*g7g$Ua{4GuLz55Snc9?YbX~wq`vv*1@W9bk3}G`!Kn#_iNu`6 zK?V>kMY9A{Kev}arIH|wEPU~`2L>a@{~)dD+B$0ir(J`cI(Ha zro7!NmwhJ5AdM%iKkJd@q2)0{)#=Es78bgCI8@zx>dG?R>dH%qzq17*6lm`Lfe$vS?UU4Ql z@OJv})Z`|!#`g0^`7|$u@mBiQ*p)`S-CP%}P2QYNHXAAKNMrk)cLjoG(3_8x_8SUh zQb5*Ic&0A&{gYO^r6<1-kvwI!1d*e=acANKF}mll*2C+q?96h1`KsN!cT;G`!dVjF zgDnc|wfsSH(B1?O@djNTnqI2#?~(;8$it&c&;I3h?PL}x)ZthsuU#a@fAKaQNli>hYiZw?{q4|uuT#h-G2fFr- zy%wCSq&iZy_`X=p?$)P2N2Flu7yAH5{d#53P1G--o5`?(OS5c`rlV|+iHPEmzvJ2$ zc8$ISUaPe)mN8Zgy^tlnk-|o?oLa>2zQYEdKfBZlSlG;KY9A|nwp6xeUz5V9`hpoffPCK_`iZMoIH(#x9_=T+c)q#AYN05>C zkN&4}#sTY!yNu*({dVFdqV`Rkolt(oO!j<-4zFo2cpVoYUsYJ-4ihf^3&8lV)~Qhi zqtJi3$2?~}=}l4$24m_$#N}B{Ox(S1A@PIWWqRUUbtGv+F!%4*9sJ*Kzv%(Y(YI?r#=jyTdLN1Uu^PX z$P*t4c^Ux`;^T|Z`ssk-Od!=EjN6jegO>_TO)It2VqPzNl?^eqg%hdDxVb*Qh6Wz$ zX!d0?#NMp~?h@8sQ%$d(Cp5*^S{u?!M%EVV6dA};10Q8Ax^mBOrNgI04bfE@n9V&8 z^+19;{qpkwLO*_g`YvkXTOcOD`~RMuR!}04N4-co^O7i2mTx?>GJH(pb2M7;YF`31 zKXuuvx6SXd+0&poNVw?s_BUc#DjMfCV<}GU)K#ds#9&3JQu~@_Mr9iVCSiLFk{2$( zY-0CLKrVB8GIWhSj%TJ1XO1PR5wn%`6gEF&vz>;<&BN;zhi4)yuWkBUUy74Y-@)fq zvx0$B9^4#g+>~@5F6&Yc-k8Yk-)r||C}Xfaji=gg0YR9&3l2V7 z9%9)o2?oo3rQN>}=|5Sg+&(<6d`DYAYPjy~(7;nxoXc0T9@~{)bpTxDXiGsZ=^XljF}F@EGkeqdl5B8GYWol;>P2z4_=PHf1-i?pNvKxR147qn~iuSbcrc zi!ghmvG6fN5wrTIb%v&SbHj#I{%)=YBHyJr>EwcMuducQHgVcDcY6%m*GJ2rP|1S} z$UN!9MJhd!tGKj!MR9V2OX6uxi>?OwDq5yDS`Q39b?UUd(Hv3*{I8XFE@n==U7>jy zKQrktHF@)Dq=xUYkA(JP!L6L5^gzgo+&!A&heuDG|2grWaiqu(ST3qI!n=zBfB6xf zTCtD`k70$|wloOVRb1)1pQhB(8I~n8tg(gn6;A8#YY z&AM=gctjh$;){Zy{{*3aO=Exm=BVpwext?^O@q$1P5!=y_nuPjNiK$@z?{y{<#UI1 zbH+Mc+Rng~%gS@x|MLH%n9gFGthj*4S8pCXdLz^!Z2NRQ3(1RABDr^@JwBJ!d5-Gm zh*`_lVd-cxt;v+>n>{=r10Ul;vZ*OZUKXsO*etPXP9C)g{&Jf=`-WxAt+577&ls|d zM6ki`p}J_$ESGH#d8>T5{fvr*mgYKfg_sy7v*26zyYr^OC0zRmLPbEXcW1{K1-_Jp=9%_C&R^C$*1LE{UajDZ@%v1ATnF%tL^Yt_ z@{ZguG6+MM;S1c6@A9I~U+eUMAdO89sCVjflsHv(u zd~RUZQ%vADTEI%>{gL7J{mZM}G|Ac1=MR(pg75Dp#LwUC5g(&pIGeQL664;$Z=i_AYczi!=gy$D~V;qwUs)qyy34`X?eK zZe9n1F^UiG+Nc%3S>ky}UVB7rjADA#N5CpKZE#FI_t*i~@VO0~)Dk;c@steqeUgal z`D(q~s4?M1AuxazWf%rNY53>eXD0McBVIW3P%o*_NwkrM3_0<@Sm zV!6wp&V?^T(DfXNo68tzKB)mMHC4e+aT6uIflf0n8Of$3Xw$9~hv)y+lU$^=$<9VI z({>++j59JmH!~u&)XE~Od^gACc~X2*okt8rG`TR*(VlDP_H%crM2ihThu%`7=7~hv z-CHI{01}_s{y>RmVr^v7-Y9f49r^72g zS~4BLLn%|xLp#DCvPU2lI53m zRe!IDRthE5<7{uxxU4G|QZ}ns7kkFswc(*+-`kRqBl9tv3Q@Leffs-;X6*C2AfAyJ z+#n%^=8$K6pEDVEX>+^GwI?Rs#6Ha_OMT9BN|3!n8#%qGHBNOeKoeaW&cHS*CtP%M z=Jnnen%^$b{!^`2U9R?O$cacG=Fp*)v>zos+LO*s-Qhl?Ta}b&T_*2o^!(Z5K>1xQB*LZ&vGs?2l_UQ|;>lyOzY&|`A(Ygt2uXfg zXa`suJASQ=AoH>?PZVZ-?ys%qAeKW)-bbr8sYw@l_us#N|L&2WK9GunZ-EdSdTHt^ z1O5BfqX!@4L*=OJ9jQ7`@=xyJ<73zV;TtSzW9|%9de$%ufX)G2kFF1I?&$HkSA)|8 zZqeBvZc!H(FI3$IW7ik*1tOmM!vWrBD?3Xn!&U@${PSU{UjlpoIe{$DRS>kj+TQU`y%+gCd}D$)I=m}?_mBFF zEGj~|c-O2x0M8yHdf3 zwfL77`J)T^!Eajw0M5>B*LfZuf@F5*{a=2Lb#0!~F$e@^apA0%iSBUjCMNoE`_}Uv zGGK5mp>q^ZaRO(S{bx5kHBGGC_t~=_wEjyQ@-K+!@AlZAKq(F`CUW$D z@okC^L`}i>#PRa6{1bBzrRwg4MKX0pK>}M1y5DH~RvswIL`%Kum~P~yIT&9))74ze zEv^IJF@8X^FksTNuCC_Hl{1Nb1vicRDk!F~LGzV4jXi|Cz#&0Foyj0<2Tid8Q6|f5 zTcxb+O-pftR5RK3HId?#X_wlBMcL<9o?V-JUsh+j`i-L)OX6><9Ert;zmMAhfef|` zO4fW5V>1_K+H7n1jXM%LASxE@F?uum@;CI@M?lMeZKyB1M`w?Q*0jaIRLhl_Jdgj^ z&R7RB0eO`&lrq0QgTSuVpX(rBC8G4JHAmXIfFcAY< zA8h^Au3XZRuK#LAui2p+v;DmuE2f6~i7~or!ld6i7?b|C$i;8%xruueh=r@CHRr;8 z$jQ9T=Bj-*g!9+gKKl&@_=(@M+q5sWIaNg}xH7D&4A@yo5Qi`ZNR_Fd?Qs*I-^7xJmOGA!`J*nj?iV zna}n%N6YOPwSnYOMwgEr4}BK5@zo#lf-9vbT_c@8csMUQgXHV@43tJb>|SaLA)=1+ z%^2TjiHEh$H8DWdMb~V^?g5FeAodlS+pst5Q=x<N$od@*s=9qwLj+}O*R zp5rK)FkryHlcz`YTs>i@7^^#Irph_M-oY8sAJ({UNr>}J4wXy6y!qxH+P8L9 z+XE5r-Na?OI1r%kqRYxA7p5lmbEUY*!bKu_B3F;`OIpWg*lt|&s@4q<_MzdvoRKj; zD9tZtZIS1jhm0-N*2;lC$aVdLzsk)jx_hu)=nzGZI^;Fn9^d7EG{JNa!r17%G?8B{ zEv+*9k+qYROV>3ENZz|%Tvs=A-#jN5w$$ML;**+lNVHq?D#K3p^Bx3q|ZsPd6rzHEtG!%r{C^5#F(GKFdV<$+_u5E=^lzBeniK>Xj3law&#I(=!1sX-L zIzx|`j}z_kSP9+duW@Di@a$4^>z~lTL6miGdx`v!6U~bUe4lR^TU$7*vj){FEj34z zYH`Cxk=IK^vvKX1p4|5{Nqeo2+V>W6h*l0nbQLsn6+I+zsg@0MD(a+BEyv={ZD);-5pSxR&- z6Q7En+-FZie-N*t7YZw&L0&q@JMYkc;%L&|j#{J}SKN{&DJs4uUAK^+bfWEi`>*a2}q`^j_0iM{*ne#lD(oS|1LLUhU^mFufnv zr5@@r=)NG)>Le~kN<8c{8y+Id93}?N|Ck4WN``+eZe=I6b&K3QdVDPFkI`caXGMmR zzVnhLauE`t(UX4>mc&j^#F&hOj(Cflp7bDWI>RbmwMFZg(J2D>M(jBxS?xY7L{ynK zs37<1`vrc0bPRgWt@w9I-mmtl8jL8+0!bV@gz9q(2rE)nY#rL-g{2}f{y5=3-OhT6 zh|tQj$Cx4_TzQ(lb%tZtQBNJM>c=Q_?L-Q5bAB$f9lBjn4iys?&e1(53rqz!elX0u zMLBKg`Wyl!4dMysM7|H)zm^8{>L0oS&tvY%@M%WAMFJR1__&);G?wwT)cHrr!)gN3 z1~dZSPfSonIK#)T)^i$}rl08PE=o2&lk~gz5rGaW#A-76jt5)yzO!X}HM!z>&MIzn z;RyMnWlip+fs7c2$QWSj3#R1dq$5P*DLRcujj)E96yNmI%q>&N^58ae4cIr*X8EYY zEj6Q+J#wOnDZ)IGCZ)o>DE@uceKU2_2Jq74&r7Hf6zsYLMM^Vz@`i$UG+cQXR8?4E zw9Eso0=K4Q7|-?@5hn;f7r!kB@)u#P8FzlzFRuTvU-TfPT|(u9`3h7^McN|y#;d%J z@)W*m)a-9;`Kv=^-GYXfvW10?=$>QJUr*B$Y1vC$+gA`Rt`y?P$Rv-Po;rf_p@A+{ zTTO^fv^^j}$45RaEIBejIc-{T0L=iuw?EN|De)QpON-Or817bCugKc~#RDJbZvb1w z$DhRlZJWJH?hof&^Vo!Ku#cRwu%KDpflJ@sN+tMhvh{QeW+X3sa!Z8X7Qq^4B1cM* zm0#PE2_8i0MOjAM({KR?)>*9P>stWjvx+YV@s5r&+Sql{7p(EKdOZPG&5tD+OAHbQhC#1T>W&Rj^HegnQm9Lqt-IJV0xQK+PlZ_2fSD}ej=1gRk$jS~9^IE5Agw!YV>6NS2a3bIocrEqbQBm%Rg*hpF%o&@8J{)qj6;y&Ihc!u$UQIRh6bRD?5G zKy{efq*(W@48{{tOYX9&X7{T`>94h{1!{9}L+WZLTI1`I8SBZ4$?Y%YrQ8QfvQRvj z$1PdMjsrR@Nc;S(&ULOtXc9E>hHZHwgE3aAb6okCfJf*BGG{o3kjR1_gS@T@*YGux`Gq2OXDp5+hB%!FO z)B=#D*Y&_<#8NSgcX_IVD~TbX@I&HO}OmNLKYTz|2In*-{`lL@1wg-;Vp2YR}NK=T(B?6EYyWg&`m zg#S^h4YxKA>y%H+0P+#DHIcbM$?eOlk$cweT)GI+inbk`QMP zM1Ry*$L{{yEWEWxvzJMv-)#Xoc`Y&5_X$xpl2)rS4xA{{W?6bEj|f^QX^goRzH|GC z1Snpa2WcN67ky+)CnuX^Bf0`r`jW%)ZjUC!7Qig;zSR0X-eVU+U!H#)6ZHB_a)OyO;aYK^UIj8USk|PX4F1pK&>BdBhhPY# zj)%n?1k*io^zQvOvpNFewUU>CfGcZ8`@rbjVd_xo7t8F+bF1D|nOop=yLq5@I0kVM?XDNUYjT|NNt>`gj*_M>$sL{H(wsQ=Xfj9gQnR3w?) z(Q0QTeo1JNB3i?@8?uz-yJHGASEyw$MH#(a)vBNw7I8yT^sR{N8Jt3 zy&b4L#AM}psf&2OYHcsgnvL(xBX_uDIJ_oW7INa#Qt4@HV;Y-d1zr+emo)>oZ_Lj) zQ{tpU3OJv0V-$)yZK;A5W1w1Qf+1%>_S8Ew`6FVn?W*K8%q>XDYRO*B;firsHtLus z>3i#iWwOc9jSzYlDquannGU1!d7%lJjDL-LKlm^y8c9IyqVrF%*kPdiRC5LL4 zX?k-GbuoYK+r96xIZ1FlkD3gA{n;)uQET|zhTrb6RdWp&f(Mh#yZznMt^-kld*;)#*YtV=%U7T!z=;!dRj-&k24u8@YG8=5bCC=;$_an zY9Ucd*`+K9YJ%4w@6L!kR$U0cs40HbuEcHVb%n&}tJp=3l+?-p7u0(q=E_=nyeBf> zI3>r)aZ|uNoNbK4(dsCSt(-!{DfGVSn6oDZQ4(a6tkoJ?M286jpXZSD&9<=q0Xtfk zYQ;&(;?8zdH)iM-#ed26>|9#1o%r$)j>$5dYaCcp|L$IFGnW%A79K3NrF9DjN+kIo7CCe4wWZ*}?I1kz0mLc| zq`CEq(p9qtIi8~SWedkVTsaEp8uy@+xm@xXrH4D!W`3?dHG@JSAq~_5F$yb zn|KPO)*w=Wy@Ros6}A`b)FTsJby;zY#v8+)9-=Tmw9+`4fq09oiYB1xL$VQ4&^0%I z?=iL3f`C>;j47G_sQK+>G%O!yD!fl~PPN2U+aB3WiK;jP)|P!ilKV>;zgJA+-;6}7 z%>4&uKCE#&GtZwtabDCfO4}OhUZxEwAiKbsA?u!2wBX5^AwwN(kFF;1rmQXQRkIrLry*7h)|?PTX=i)73|`NVl0O4h9`ZFqmSUL!@2lyN3c~09FiOk!=6g;Pyat&{ z0CInlcR_4$Ur5;4j;3>J7%fIlV*p;btFeFdz4BAp1kz>+hK#jNhJ(*Yvxv@j!51%(f2iQlu9qDiLj9^5BXM{=aYkjIro_K3Lh^T- zWoBlbFCL?4gT!uv55j@Ea!;4dvx*bN2M1)lDR=rkP}gx4Q*5DaZ~?J)bp9;A2xo$P z_Bs~3uBkNsnUVKWU56}JWcj|J_l^_5L-;xu_=P!Bq~Av0b%O%hKgOe~3_x6Hn&YJl z5biU_O$c)Xot+I(`{f7=4ZT9RVkqud_HL*fC-nJ@#siamo^jUZ`hGsMelvH-5}Q|h zBbtE$DZ$p^CA+D+9B6H9Y@xEb-?$NZynHdkG@kO?=t-)~XM+@XfUC6^51-r32yU3t zz#7fv6wDptOkGAT4^8__2XI!;(Vze2$k?Qhw5iPP?zQpKe>?RmvKE|XXxh43<<^r8 z*6xji8%r4)+z~vY-{V=XZ?}nK?DwXK0^ z4lOImDgqv0fhgXZ80HdFr|pDkDV?m)@(gmc1CA?dXnZ)!qg(Ka(Cyj(O&Fj*-RZh^ z99SWFnUxL>4=W&68Zql}y&(^T`U_G~*vs_UrQ&RwPCyMSoMdrj5uDc%ZMLWlQhBRX zEfcI`nF-uj-O`6c4gRh2|C?@rffoX7cUl@)c=qV-c$#4fk@iH7!;XZtP2^#Nyo~6| zbdipiLONQ7tj^YEnz@ggIAi3Eti)eSClK^^nFfEV4k7MNL8@TV&Cp?8LQa$&@UYLE zsptTJ&wO-9Eee(xpzm24Oiz0V0zIT=yD{HJr$wV)xEwo(yi7{zth-rYE%=w!T7p$v z9VkalWL^s+DPLSbGbYMCv(}ESjPz>maF#G1$|R%5j}P1@wu_%51+@Y78+8p`lBGIY zp2iZs>z~$G$-w826BQvYJphI0i@BE%6f8c4NWsC)T7rjaX2={pXjv(Kk5I}9px?OF zA1xf;brWs}*RM?N?8Ms{KtT%S=#2i3B9i76$rHiuW7R#%`tc6q75(-Ay(78`R~~Z^ zkt3d;_Gjd6*=7HU_}^zHSzyXxOBahu&o7`DXpBACi|d-}{mK3Z%l1-Ww60B4y_yJf z_010-Fyj0bRA2XQY;-*|hBwRl-$ejqKz<@Z#87jyHN{3+MZ6@YEN^CcW-`6eK=;*- z-9p>V>^H3N)W^0|b4T00WA22Y4x@ITL-~>Flwvu~OrNV9ka1VV&~F(+%Lzhna!NR?zgt_{7ohdv2Kt{q=be+3w8zuwy&-7&>d`K0Qq596S+UJihYIt z<_QwAlW%)B0Q@U8QX}3?XQn}u62-bB^u6Pr9aEp(sNDL^saHP-+kzny3i1*jhS0Y= z7dSzH4kV~1%mTDd9YKwj-I!YXnmGfc+`voUs-?&;3J~0SGeKnDaAg7tw`?{BhwoXJfdLOuAZ&s>;98f4;U}HL9?QV#guE-+ULocutWx)& zh8C0a(i5J4lk_KK-l%l}CHaSC@B@f|bVzUS%l|KLyKH`e_u+?s(U~7l7oEP>PTFXD zy`9snM%6zqSlh)g{iz9P|2%x}Mt=?Tg*S%D$-;kiNRA?2z@SFvUg0nm#L917uHJys zgX#1Ykz2QG4N$4NTJ5lz8Y7^l2G?uL`70c~s_&+l^#0FI?Bep93ppM1Z6d8F?!2z{ z50$rQh28TqxqIy~FpVmFa%VgE{;vUHE_sh|9!?idoVeA+eQxWukb53Bc)<1Q4`)1X zzsG#|^3THddK^F(S3JJ2dp6nE31}9;wVT--OftLxCI0?2FhP<*PcU<|ZT&0wc%gC|*K@)B|#W|(}^HSk05jLP3M37#+zDcXcc4pvab%`D(V`U_4TfBTJk zfpT!=(y+tdqzW`4O;{jld88peST3u|fQlFAa*}zKivae7|h}(qar_~)aEXb($>*~WBi)pr3PyHo%{h5@x8lQn0#j(`~ z5SE-CjP+O8o&neWHrmzs2D-rj<;{39B#|JrtuYfnL#!MMSe;2X=2wSSle2v|VMsIx zG0t#q6Y_L%7zdK+TwIw`zXv35Y;Euih{hViz+QL-zmWx_3v~k|sr)6!;3N871fj9w zNdmiN@zZ)ED=OdI^T|g-qfHC)DChtczy_$q{@YKQcEGbz0sz7h9-AYUyhmXbq+NNJ z6YvaaIa1cdA5Qc_mH`5}@y?X2JlVBrk7$m~iA^i-C_*&O{C@C^E5oofFMa6BdeyqQ zNmq!4n*QEbzGJ?lkNbb?xXVmD`0MJ!@8Hvv`eM2^+YADf71QZT>J|MfY4&{{_cO=V z2^&(`lQ@QM=7ztivqQ1AGgpi$9uU?qS;{cqR&Ezf+%-Fx=WD7*{J>I*jWN#nw-?_x z^;apcVz89N9)m}d;fWgi*xET`kijlFdt>sS<*_=kkruke^JW|wOQPw$(HPO)z+&K5 z@r@4e#m)U3_$qpSVI=I_E>96*=k9rX*5V8$re2W#=`h zXkbd99JbzfR#b! z+Lzjng_ga38EA`;;+buJ`?KCId(!$ zO;;s5-*ZPHjLNgM=`tT>h8uw<*a?~d#Bzd2{-3x#q++{Z{r|GtIB!UvLT_H6y6X3- zrULi+yRj%d5DhpmZtuV=Z#CiWliT<9KX**YBa(M*A(gNN`{f`Kby8}1V~F-FoEfFF zG5V<|`1F1wpn17McQ-uJEg^<>kKJO6LWQ=o3ILF1#vdsTd0+BOTf8_yhNW}8?nt?xrX5A>f%4ccRg+j#GGy|SbHwT8@d&U8IJzjEpD zyC9Caa{LS@v&`K8_MMl$X(Rybyz*kQEh8l{^qxNik8j|y1HTh-$;a{Q1h-^Vt|N%2 zQHViOx*GNPh=_Ki3WteC^Fp#8;5NPqP3QB?bKWe?om6fgs2`XXuw$+FQt+W$M^{WRAweK7|4GB$}moigjWJv*JHgIv`^e zGmrX35Jcf|u8hT7^<}{L80Jy8wkzgowB7}&mb^Q?2lnb%_wX%Z;rD6=JSHp~b=^nO3iDk*6 zC%PLy>yHdsP{6gX`B>WqipAgvtX}b>ldQwMlf8EK4pYkX1X= zaJ}pH#sQfaW^6@&bm<$U01r`t6J$m{#V>|KCj6_w{Tap zYv57Pw#3{cD6`?hwQXZ9#WZuXjp0Ii3VTb?jcwU2L$V+z zLbj)mhcY2rv`l;Xt*Ep$QNeE+po!Vf$Q=P=8J{w_Qo?khQXB>j#Qx0diF$ZsdpJfo zM_HLCb!DuxIb765|yU%r;I)_Pm2)XB=QUUx+_%cs*ZO%*7S|CPY+yZS}PWKZp<$SleV!66si*p7LQv%w}JvoPcX!^P9tES2_4r zXB2FHt2TXVk#2tW{T(|#HrVGo{CoAZ+}sl$9qCi*kPE&Y>QSK2p|!^SIh6b4?NQBZ zf6eq=-K(;;w%)ZLM zy@{-@+z}3SZr(YGQmk+!jC}M2&gb}90uI^7!#(EE=l|JC)~wNymcyAs#wRg%y?x5e z)whosv$>bL^9d36TgOdPY*@Bgd9I|LDaC4@C>4que>Qm2Wg4a3)3Q(?#`#LXhFIu& zbhq%GI(H)$B&|dePD79z9E!rpeI@HJzR^WkO1LR}@x`nt_t~kaX(2@`!6So<1LQ-R zE96)}5(*KO_Z_F*OsTseR?#sY-dJ~X;1;SnfO!Bf1oCkQ6Y0b;!!7UO-+#q%gFGcJ zTs}yS2TIpVzw}}S%}){EHi(O}qw+J5*7b`coiAvGoa6q-o%mwn-5cF>P)|p8OexvN z?bD*d3F=K3*t@^Z!_MV<%C=BtejGSS3b%`zVT#N-|8#5BLNX)1Qaw+Uw;s^sVtdTVAz&cM@w6h( zbpYmZB*P>&=!0R>?Ps0y2dKh^<5c^bxXN^(q)I(yQp~JWtKG1Sap?nT9|iH*lYiEO8$xE8hXPPr1ml6P zVflcokl#wub5A(gM&3xh?9r7y_TYqbVRbU2ob|^j{{1xwR zy#15EkdUp=YjPrqV1TFuc0i6a}=ARR`V^VFnk%cUV^L5ssd znQJwS1?rbiy}q=12xNl0?Hpw17WamGAHbc0U7Oc?n3KN=U)?P2rH@C^?l0#37$As1 zjQ|hC0!gn1wlVw0wjS>nytkX;Lx#%wUiO1bZ-BhBFuMOGj^r^2>U##Ua?w;sVWdQ-Yus#oDd25`eT}XsoL2A!+QoSbWcc=Xz>af zqUr17dnPaan0aE*WN@zECD}P1qChs_$dh{Qctw%%u=oNny{fS`0+mVIOC=H+G}40=(Y1F}%i&aE_CQ zIpdq#m`7~SBt$M9p7a6jT8z&g5lPWIUl+OrV8Fd44U(iu=o#l+A)VJu752-A&0?NP3rqvp9( zLX>NV$I^P4=dh<+mXonX;A1zbh_v=^j~~q$&(Pxt(y~V-z3pq239Sni zufRBZJfkMqZY>C3z<5k&cWzmoD$O6KlWixztSBv&U5M{0zpudzO7A=sd?qHzT(r(b za9DZly8VxA-xuLR$ z^1K=YNLhqf@bw14{U~YV(C1xYikEB5kVykizX=1_$|5&uD7vo#-53?HD$TI+vpYcD zZKh)J|5W$w;ZUx9+e1lF)GAbV?bcFcQ?eU-YH2NnV#eN(L>QZFvYBB@Lq)V&g=90b zY_i{uvJIgKBiW8k$aV}S#>5!&UN>6nX+6(ze9wD)-}@fV*Poi>zOVbbulu@wzwBGT;g*+S+dZDCYLh= zG$@}h4;Mbu5CdbNBt@y;&&#ZFb>xFA{(6wg8yhHM0ntWgfbE5AN?TfxjGM_^XUWCy6KD;Bggp&ou?4#t&(YRCi)y&?id(rC0zr#+UBx$u3%|s zZQ;siO-!L5D>O>LFlYwV#YeyrMhosY6%y48gM$kKs44Yn(uQHnRhKV!KIQ4w!H?kS zqnSEnVy!gdSaeGfeczeufymw${(_Gwt2M-wYEP~|L_=B_hvRM^uS~B;3~AtqlI?|~ zR!~g9?6!X(-d#AxXzNLjG$-f#`(!E%e>lObkrmrqxH3d`w8L0lFffR&8b2#Jf1xGZ zoc20Ip3{kpQgaQ}nYO8${@9C}(2tvD?1*}>F!Lh74I!NX7djOn8rIhhu)&!@Q|98F z)QUK|?@P*oD09a0haECPe}2J8SoU5We9b{O-BN{Ok3}5uJM_>@`JN$zN zUp%ZJ-6Qg$``eL|R}gm7t6fuzoUi+79E)vh({bkZTJCX)C!XN-2Is=5m|djoq_9}>YV)HV{wV;vemBDJmo%?l;*k=J@^9O z%}jZ;IGp%$T3%yY&S-&_{*z3>abP*#A^k=&%dZpL((UPtN?>SG^Sp)f`V#Q!RTLoq&T1r0y6P=l8s2kNeXg~9^)NQCYLA=T%u4+!aC{b@$ z$n)10sFw@%yNeG%d53Gr7U0=`+fMjKilkZ9^mJ6K*|prxHlny@f7ntgb(ijs#hH~m z2;}uJMO2F-k-uV|h0uI9jfoNk*q9)=!2i@;G#)* zx5`>I-r|!dksg4P)NOXM5$R)UU6_07`AI;NTVvP}@9_DP9e^@RftpeP@u@JFA~0R)FP#^49G zg_Iu5da0s8m$yEuPV<`j%@v*VWTY?^^P)axl4P*C7|-CMd2S25Ug7~L^as<{I86wvRSA*;e5{9BX7E`oTW8aFZ)r$cSlBuWQZKmddyYhdE8piuX7gx zt!B!u+M^Mo9=HcxgN~hLJ za+YSuqlJ0>;u@tsx!1|GMl;|3(9rICSxc1XUcbR_71CzB+9cY5J^<4&$(vm`f#~tS zuQN%iN>5T%ml=@JZ42y;F@mAjIX&_xEnvSEW7SV}Ojd*F0nnG{!IS2oVC zdrlEafwCb0SYOi>_?dW-mE9B8mWd-8l#a*vyatX6c(@DF+C)&x6mJH5$SXk@P6cigqEVd(Q(k8~C-M_~hS+^a_)=M?{6@5zB_EYk zQ0w@g+0ESy<$nWw3i9l4tpWaA(u+!zAqk^myEFD`<@tSjHmrj^)kz&5aV!t@zbhN( zIJqD%K!_ej89LY-kUiv1WO6!{_>_db3T->0AvATalZ&@`p4viU+mCGlEz1wq>a{(2 z!A@8+MNof(9qik=o_*Hb0)&lu(h#xI&z~V>9SEFM3NP2>poqT;Z|RfK$!NNoP$7A> z3nLr?A52Soan!ES#FwU<=?DWB4XlFzWKtzs?gjr11YRXnt zsP-cp4eYHlz0z9=Zk7^xt%f$BN(Qc;Ru}p}R&0Yf*B6`aT<+YwvU-`2dMb%pd%H@f2<|}<)w1j_Jlk5w^biXP|{cXJ6W^GmWj{3<7FY=qP%9@5Bdp%_|>H(YyuEkGu28)zT?qpYtE+X>_p zC~6nbaQPDZ)+?D z+%V-&mgM|&x`|U7?zA{#|yo7wRBg2cq`!Kni*Zgn9e47IaVW&^FG2(=7xt))B2eGR+8-; zq3g~F;B4Y22sC9rdMSC82(xH*>fzC>js(p>@$Yy-scZF3pTj{9@fSP*X^p|Z?Q1LJ zWgfw~RYRMrDNNd=H#O;CmVJ6YXt=IFfYh@}(4fjI@nlG2IB{zWS>o8Hpb}M0l<>H)s+`NRGm_ z=_|OPr#36FKH_!e!l-tPxuBNI@u-}RMltt}sEgoC5S|W!m9e0hF&-!}T9bB_9QLsz zz!A1E65r2j-H{`KN|@)uTI}IDHyHzV8Rmi`47ZH)+JmdnhKe=N9=06C;_|&RJ?U~i z(e3DqMTbuoyXFyIUJ%#Pr?#Q48fi8bv82vNGDas**>STw$CAecjz~Qw`gq&b`H4(r z!YrPAv}g_MR>nYM2m-0Q$FlrdqpqFn|ZQ;dwUqATE-|J|fKYR#B~8 zxe-A!Vl-K0A1CwI&o(RAU(iKfw_eNQnSIrukmn728`X039Qg|mAyqUbdZ95H?OfIM zq)ZgmlP`%QHo8&voaN60ukRbV6u0g{f%X{WbTSsGFEb;6khwh~NI~PI-)Tb%?ub*uS`yEr&N5 zAZb(J==kRQcj6oLDCG*z-ev0&fSEz(XNkLOf`|LYinxVoYqRKvFZA{npe|*Du^%ow#gmwxmUfK}`5uHpz6SuE{YxkTSw8^$_%G`R`o>8A zl4f*478775z9|6inn5N6{{t71Rl!xwt{5KX%UPqtO|c<=DOFZ%ZonS+sZfOfw??%{ ze)sOhNKe6N{p4>2e@$lnZv`p!fpg+gV67-*@k4TYE%Rgd&_?Dbf7`8- zqhu*kN_9B?L-&;7(9>VfmYX(jX7l*NkqYaQ}eu1p&Yzcq1nA*#IPMR zU&CF0k9Lm~eCQ4+N+NDTi9s1Wp|pEDg57qkU)rdNws$mHe3&T+UQONcO;|YVRKH23 z*4PG-|MA=t(!nZsXJ==mIOL(y^9>UBIlSk=7=+pb3s#|@ev+u;dXN477YG(+X~sNQ zX5(HD&kD(itDjcPih>^6^&4(FCniRp*d)|TjDl+!5Q`rMxvD@E!2kZo|3A2|{Ck%U z`w2H#!tA^PVR$RMq6DOnxsn=1lhGrg!tOG;0Lr{39j<)*Uu?$!ZRCjo<%2$Sh5EN^ zTk5+_b9ppM{PhbMF(5^{+m(svE#HJmh zEal>|MpaFl62iiCE003U)r4~1l(eALm50R?S}^-Wg#?J&Kluf3@=159L3`jV)9 zri91;9M{o{F)jBcGBrSKdKuEg!dD z@}{Uf4}*&#Q%qs#SpX8nbB@Ulm8zLsVoueKr?nWiO?eJ~q66HbZok%$4@3R1noYs9 zWe`#t07+6}e);7FA{cT(_R!Z5r>S9Gfo4zJY-YgVvu$sDI46}DRRTKeKCxymH-l9t z7?%7Yig=n2Xg~X{VS4A z11PSjRpm8mcDU$Z$)f)71UoLzp~Wv~|G`hS`#Z~=u2fV-S1y%N${8iti^e@}rRV{J z9~VW7BKJ<9z?&vF00~c#w0&;S*WV5-pjYm5SM^p_0<`pgoj{ZlA0ZW?%>*SP^8hv# zQW{*GIzUEPo(53p+DK8j`!1@IM(ah(pnYk*Wov%LR0q^@Uj%jKuy$ro@z~P?_KFN1 z0=>F>x9AWEw9RJkNdzm~zvSa3Fe(?Gjc|7?6h|x#hbnRd2gW);A{HQx!#t^Js-E!%3X#JLXfkoZZH-% zJJ;Ab2;bJyfkl@u#O6Kq$yR1>22_GU2fcD0QGu{}ClTj6HNa@y?srZ^tNF*$q25YS zQ>8{TSmh1GM4q&gDlYLPgh6Xl1KXubeWU8{>N;8~YKTc&`l^Ks-Xx}u%>nX`LCi}8 z88xG)ol(XBSs+g|D!~6{Y#x2zvBtXVplD|Wq}I`w&P&^$nLQ(P6xw(HTrcNAsWKKd zh18OIO}DVk8tX#)xi&x#N!8~t7x}t!SZkl6)cz{v>b+uQELRT=17+CqIx{9PQCazz zMtn#&2rrh7;*f_9@N=K)epqM<8V;CM@08m&;_Qzn0xyFcsNI0MqD(OuSOGfF!nE^! znJ9lv^3?Cf2}Wk_4Y=63<0U;2D;3%zh@1LmF8r z8Zz^TwTs`c9N*gQdE(6nH18vp$6>+eWNh_L4sTWH`vbHXuLeS;96J$SyyB2YNJAjZ#-eWyz&MbOI9; zKXJSYoqI@ReOx$%srOft2H)5LuD1s*>Ox2I4=Xl7iVqWY^zLoSiM?~j?l z)KA~AMQ#_d*Dj(ij3_$t)$9p-cQDt!W8Co?x?7xW#pxsr{P=+)-R`L**j{)d>8Y){S$t1GFLM| zYaRZWyvkYG9R$ma4XR3N(Zp#=)JyW_kB`az*G55h$$VdGwI zQjo}XUV`*0aag12Had9K2Mq4EE{As~{s-f)6GT&3r5kG$5NfJ*zu$#(6ie^rub~yi z__kd4Tel>7S+TjfKISN%mD;)nf$g?8bG37HMsvu-FzOrUxBLehqALL)XFh|8txy{l1k8Hte^Ax+u23J{U!qP9jqWfys)38l%Jo!+)&W@i2$jaq&A8~>-u3aT)v_#cQ zJ05Ehx!emNZS9;WLr(|G>@%}GSC%#vJh8<|x@az8$Ty~4V}w_LXV=i4IUqd;5(m%~ z5Ry1d0DT@$oB*d(JR&0tcfM5M%Jr9#VO?q>nGaYmNhbuRorP_9?}8a$*}dkBy4?A} zQ_l64UpA){Z%(R=jvp=7(ex~(Dkp2XFMX|nS>Vtl<9xSnlVz5|An!iCtgEs39DJqW zUwY4Q)d=!1VFB-`sO`7BDypnMwHbq@yKa0B1IG6^TI!+<=F<0ipqyb?KkqXpNOIwT zz08ZArQ`ikQol*#mMn0tY^evVY-cCY7?7+hYcl&64&NpEV7US-_@Ku+skVWnwg$q` zlDH=a^;P(;GhgTo7hzo`1xkQzS*^6o#TtsBRPCgTSa(Sir;M z=aHa|_+$Ix_y=4Gyy(2vs%LUBjn|wqc5&5vX|vgW$`2%U=-$7)&7Pi4QY-B=T;CM9 zwr*?+$vX&Pzu*mdB1f+uyap;&{FWh5kbF?9&ebH@N@3hflQb_J(!Z`<+qrIA2x(08 zXRM6oJLR^ONERwAh6Wv(3+sK|44qN>FzhlxuBX9uRw?s-u6+Eu%+6}?TV9uIWN++p z{^3vdMx$d80QB!gQJb2U|6bzvpLFTB^#%5g_a6w0OATTf)|$NFt;_t4|7UF{@QbOL zRJ>y3Tw%5fg=h{fgkQm2sRe>a>5<^->{(i&880lqCzF@0h`h_+LcCc?uV_Q zhsRgeTrjGC885p}!Z7h<>x>2J0+!wu{w^>tcKF?sq?4^x1*yu|m12~rt@df(#magA zuhg)vpR7xVY56BVAJ!0R_Yb@6_~Lp%m5LC@TmJQh^)2HEK$E(L#vYyV7@I*+qLc~t z*aP*vN-=n!V)gyHl$83?k#KUuDV} zHoxa8p%g9+-nnR*n7E`DbVD$%f-%Dyv#ft^sWBl@$Ep1{LL8kstq^A+tKgn%LJiKf zYVw`2I&0_E`zmzY#U{b)H=ECxSy&(6-v?TWZ45tbt7wA0+G)MABb)Z2xm;&Lw%+>Y zka54?LOScST!I|8ZICk4W!I|$_|ED}_1ukoc!yR|zm~SltMyy>1u05vuMOSaG)4P`O|z07c91e%VeCQe0UjgNV4~W8n#;=IZ&cR*%C4gL9Qdu)a|d zGqP?KFXYt|(>=U9oT7Qb@`|TS;=Jt}nyjX`GXlK;zM+w(4|BrE|yKZnf3(1>P%OAIt!c ze`NvR#NO3`em8!C)QPnp%j6F4>>%&4ES-xzHCHu;qO?lsCe=8n>$_EZAp@-yD|6BM zdH&d-^jcSNG)Y%72M=^8v}7jHP0=zXe`BZvPQtrdm*0{HrUGo}Y`I1D%e=-IYf$DL zd|w>>|I?FNbhaLxQ27}Fy3RH}Y1e6PK(vc-k*41_B-r_UH1XUQrZOFyY3wLq zd@&O^tScEC8`pyhfZCxU*6ht16l>blF3yihEe)T;&IJxvrIBy=+}$$y8;hySjB3Yb z@${FW9rs_+rI?kQ?!P*zuFH^+kf;mb$zSZ2Or|I9$rI-9F1XSea@ga;7tfcDCLsJ9 zFGit_n1XjaJJQvg+%u9seV;T1zHn z(_vlKHQv#%(<{q)>f@`s?G)w3#G=oUs^tv2%@06tCo~yQeNWuWPu2_Z35Nb&Q}hf( zs1gQ3!iyP&8QEp$HIgC|KCgGTNSB~cCD}~DH7Auy?j9_cvS&%^psMS3 zK56^p0GlN;~A(Hq%mjAyMS1XwU&;fXQ zt~#PEC;DPS_C+gknBR`{c4Z(hQ!$(a3Yh{(;p^&i8LiR6p#B+@AB$oFSm*5(iq+ z3e}xy=^iS3uYg+;2oapUtBfNadP~BJKb}%=xP2NeArOb)BAv<%r~t&{R2BcSY|9-n z*^LKhh<~+aC}g+&T3$I(g1X~RE3Dj}6cR2QDFC>~OMojt!f7gVP!K4vtd*7 zkHKRHoiKvj)qzUb>(SS43l+}E!imn%U-!^P=cCNq5~_B05b1**{&}O#C!Bywz&|QS zfeu6RWwx=Skg;S3yw8G|{ks!zx{Hhr;>n8y=IZOrtM%gB4dyjv_{>N+f8YGU6_+}X zGh>&xOe%XD09q2wP6U0_JUgGKOX6m&9?rDS^F}DRs0LH_H`l%@Y=p%~2uf0ld|k5_ zxU#G6((U(!mjSWqB?pL_er=L>>0)@dehY7%wY8IYR(I=a;Tx#xn)_O1Lk!4gNx6&q zFmj}3L*b~RBHADBT4ZoYEamT{YDQ&@dS2~d$UYqkZTeNm=m+ZQ#fpW&f3fbnfV4@j z5IyY(>Zp-i#Xe;U)!ADhYxR%jMbpp0yt>Bg!``+%RU+=2h!XwsyQAmq>%@_nDxuZS zzwSd?{|Y$dFzY<;(2Dcm3vR-2d0zk~`ux@e<_q8&7Q-`%zlCqgd|yH?1DX{(n-l|B z<;X4fP+ofdu25d4 zKD)1}?&I$1JJdQ7a&u_vf~NYVcuilqtsm3HScdiQ=K5#4;_q?(RK7b2fwI8$i}HM` zWj=~t05*h`B|fMV2e?Us5%oe_I?8D0V55wPG$6zX z)TZ&wq$204(80S>$ys-|IQbBB>Zqzj-(hW51%o)xK2UEw@EF8Pcbkj*-5DG_e&B6$H%&uR?Iw$PS5sOw+ z^n)`>bzcsadhJm!Z8J!^IT`Nx3ZMyCb)i$^UqpHW8?CxH_W!(*4}AlbLAI%zpeYQh zt!uS>GGD`N{rtzDI^Or8bP2duEvF?Z$Tc+p#rg4&zX-l>yb@IpxH!d#1G5q-8_+7; zh(jIals(qz;)uEcwMiQ1|1R?R|3GXD<%^{M8;NkF*a4~51l5VQqZ_4ikK$Fl(v(m8 zdjClXh~+1ld+QgtFtQ%3u0B$|hURBxflN)^cgFA3DV3?V%^JH$@oGMqer%R~(F)|U zUiTBnBCc|J+C}GK|5@O(AI9ngsFwkNqa`O7Cfb+_zd%EW;Vyp+Hli=QFiyWl|3_|M zQ5T^tu6b&dt2LnO{Mqad7IxhqC z*Ky<7SeGTDGHprs{%E+N>a~=;NxR^lf*e%_O2%Ic zt}k>HjStTQbo}4q*}ypL&iGP;N_xuBfoG_37At5wHhtG$RlXE`n7XZI_2(tI_yz&- zs?+_|pr$z-XjPq;>r-#G^@%N+4%vcd+Vp4#zV5n&Jn!5Od10(>rN5lnvmrNkAlv=! zms^(P!`*h+Ybntd^k-vB)Mo&P{2k}VogMnY2)wz_;r$hV`>+2LI`H2nCRx5WZe%J$ z%QfI?wjX2u__{~^6~OsA(FU1~_kV|&T3v`)E#EjRr()6#lVk=peJhmJ0*>7wO|&LR zt*=(to2T-Q_W`quKV-q$Mi<8A zE503=z86Su5`yBHx9vbWpJe%s4PJfYH>vlv3i6PbgFNIR654sE5<=E!dFT4weJ=f2 z96I+bE-tRK5h}V>{04(qpFeB=YU4Y3kH7csod<{4(D%PUXeOx#b|vX=-0N67zF99S z;(L1f9ko16kL7=C{Z?AiACu<4=ehrr;1g(!|1X6EY)$dJg5ItevOaX>U(Okv&DF8J F{a?xN;Cui8 literal 0 HcmV?d00001 diff --git a/0.7/assets/images/favicon.png b/0.7/assets/images/favicon.png new file mode 100644 index 0000000000000000000000000000000000000000..1cf13b9f9d978896599290a74f77d5dbe7d1655c GIT binary patch literal 1870 zcmV-U2eJ5xP)Gc)JR9QMau)O=X#!i9;T z37kk-upj^(fsR36MHs_+1RCI)NNu9}lD0S{B^g8PN?Ww(5|~L#Ng*g{WsqleV}|#l zz8@ri&cTzw_h33bHI+12+kK6WN$h#n5cD8OQt`5kw6p~9H3()bUQ8OS4Q4HTQ=1Ol z_JAocz`fLbT2^{`8n~UAo=#AUOf=SOq4pYkt;XbC&f#7lb$*7=$na!mWCQ`dBQsO0 zLFBSPj*N?#u5&pf2t4XjEGH|=pPQ8xh7tpx;US5Cx_Ju;!O`ya-yF`)b%TEt5>eP1ZX~}sjjA%FJF?h7cX8=b!DZl<6%Cv z*G0uvvU+vmnpLZ2paivG-(cd*y3$hCIcsZcYOGh{$&)A6*XX&kXZd3G8m)G$Zz-LV z^GF3VAW^Mdv!)4OM8EgqRiz~*Cji;uzl2uC9^=8I84vNp;ltJ|q-*uQwGp2ma6cY7 z;`%`!9UXO@fr&Ebapfs34OmS9^u6$)bJxrucutf>`dKPKT%%*d3XlFVKunp9 zasduxjrjs>f8V=D|J=XNZp;_Zy^WgQ$9WDjgY=z@stwiEBm9u5*|34&1Na8BMjjgf3+SHcr`5~>oz1Y?SW^=K z^bTyO6>Gar#P_W2gEMwq)ot3; zREHn~U&Dp0l6YT0&k-wLwYjb?5zGK`W6S2v+K>AM(95m2C20L|3m~rN8dprPr@t)5lsk9Hu*W z?pS990s;Ez=+Rj{x7p``4>+c0G5^pYnB1^!TL=(?HLHZ+HicG{~4F1d^5Awl_2!1jICM-!9eoLhbbT^;yHcefyTAaqRcY zmuctDopPT!%k+}x%lZRKnzykr2}}XfG_ne?nRQO~?%hkzo;@RN{P6o`&mMUWBYMTe z6i8ChtjX&gXl`nvrU>jah)2iNM%JdjqoaeaU%yVn!^70x-flljp6Q5tK}5}&X8&&G zX3fpb3E(!rH=zVI_9Gjl45w@{(ITqngWFe7@9{mX;tO25Z_8 zQHEpI+FkTU#4xu>RkN>b3Tnc3UpWzPXWm#o55GKF09j^Mh~)K7{QqbO_~(@CVq! zS<8954|P8mXN2MRs86xZ&Q4EfM@JB94b=(YGuk)s&^jiSF=t3*oNK3`rD{H`yQ?d; ztE=laAUoZx5?RC8*WKOj`%LXEkgDd>&^Q4M^z`%u0rg-It=hLCVsq!Z%^6eB-OvOT zFZ28TN&cRmgU}Elrnk43)!>Z1FCPL2K$7}gwzIc48NX}#!A1BpJP?#v5wkNprhV** z?Cpalt1oH&{r!o3eSKc&ap)iz2BTn_VV`4>9M^b3;(YY}4>#ML6{~(4mH+?%07*qo IM6N<$f(jP3KmY&$ literal 0 HcmV?d00001 diff --git a/0.7/assets/javascripts/bundle.220ee61c.min.js b/0.7/assets/javascripts/bundle.220ee61c.min.js new file mode 100644 index 00000000..116072a1 --- /dev/null +++ b/0.7/assets/javascripts/bundle.220ee61c.min.js @@ -0,0 +1,29 @@ +"use strict";(()=>{var Ci=Object.create;var gr=Object.defineProperty;var Ri=Object.getOwnPropertyDescriptor;var ki=Object.getOwnPropertyNames,Ht=Object.getOwnPropertySymbols,Hi=Object.getPrototypeOf,yr=Object.prototype.hasOwnProperty,nn=Object.prototype.propertyIsEnumerable;var rn=(e,t,r)=>t in e?gr(e,t,{enumerable:!0,configurable:!0,writable:!0,value:r}):e[t]=r,P=(e,t)=>{for(var r in t||(t={}))yr.call(t,r)&&rn(e,r,t[r]);if(Ht)for(var r of Ht(t))nn.call(t,r)&&rn(e,r,t[r]);return e};var on=(e,t)=>{var r={};for(var n in e)yr.call(e,n)&&t.indexOf(n)<0&&(r[n]=e[n]);if(e!=null&&Ht)for(var n of Ht(e))t.indexOf(n)<0&&nn.call(e,n)&&(r[n]=e[n]);return r};var Pt=(e,t)=>()=>(t||e((t={exports:{}}).exports,t),t.exports);var Pi=(e,t,r,n)=>{if(t&&typeof t=="object"||typeof t=="function")for(let o of ki(t))!yr.call(e,o)&&o!==r&&gr(e,o,{get:()=>t[o],enumerable:!(n=Ri(t,o))||n.enumerable});return e};var yt=(e,t,r)=>(r=e!=null?Ci(Hi(e)):{},Pi(t||!e||!e.__esModule?gr(r,"default",{value:e,enumerable:!0}):r,e));var sn=Pt((xr,an)=>{(function(e,t){typeof xr=="object"&&typeof an!="undefined"?t():typeof define=="function"&&define.amd?define(t):t()})(xr,function(){"use strict";function e(r){var n=!0,o=!1,i=null,s={text:!0,search:!0,url:!0,tel:!0,email:!0,password:!0,number:!0,date:!0,month:!0,week:!0,time:!0,datetime:!0,"datetime-local":!0};function a(O){return!!(O&&O!==document&&O.nodeName!=="HTML"&&O.nodeName!=="BODY"&&"classList"in O&&"contains"in O.classList)}function f(O){var Qe=O.type,De=O.tagName;return!!(De==="INPUT"&&s[Qe]&&!O.readOnly||De==="TEXTAREA"&&!O.readOnly||O.isContentEditable)}function c(O){O.classList.contains("focus-visible")||(O.classList.add("focus-visible"),O.setAttribute("data-focus-visible-added",""))}function u(O){O.hasAttribute("data-focus-visible-added")&&(O.classList.remove("focus-visible"),O.removeAttribute("data-focus-visible-added"))}function p(O){O.metaKey||O.altKey||O.ctrlKey||(a(r.activeElement)&&c(r.activeElement),n=!0)}function m(O){n=!1}function d(O){a(O.target)&&(n||f(O.target))&&c(O.target)}function h(O){a(O.target)&&(O.target.classList.contains("focus-visible")||O.target.hasAttribute("data-focus-visible-added"))&&(o=!0,window.clearTimeout(i),i=window.setTimeout(function(){o=!1},100),u(O.target))}function v(O){document.visibilityState==="hidden"&&(o&&(n=!0),Y())}function Y(){document.addEventListener("mousemove",N),document.addEventListener("mousedown",N),document.addEventListener("mouseup",N),document.addEventListener("pointermove",N),document.addEventListener("pointerdown",N),document.addEventListener("pointerup",N),document.addEventListener("touchmove",N),document.addEventListener("touchstart",N),document.addEventListener("touchend",N)}function B(){document.removeEventListener("mousemove",N),document.removeEventListener("mousedown",N),document.removeEventListener("mouseup",N),document.removeEventListener("pointermove",N),document.removeEventListener("pointerdown",N),document.removeEventListener("pointerup",N),document.removeEventListener("touchmove",N),document.removeEventListener("touchstart",N),document.removeEventListener("touchend",N)}function N(O){O.target.nodeName&&O.target.nodeName.toLowerCase()==="html"||(n=!1,B())}document.addEventListener("keydown",p,!0),document.addEventListener("mousedown",m,!0),document.addEventListener("pointerdown",m,!0),document.addEventListener("touchstart",m,!0),document.addEventListener("visibilitychange",v,!0),Y(),r.addEventListener("focus",d,!0),r.addEventListener("blur",h,!0),r.nodeType===Node.DOCUMENT_FRAGMENT_NODE&&r.host?r.host.setAttribute("data-js-focus-visible",""):r.nodeType===Node.DOCUMENT_NODE&&(document.documentElement.classList.add("js-focus-visible"),document.documentElement.setAttribute("data-js-focus-visible",""))}if(typeof window!="undefined"&&typeof document!="undefined"){window.applyFocusVisiblePolyfill=e;var t;try{t=new CustomEvent("focus-visible-polyfill-ready")}catch(r){t=document.createEvent("CustomEvent"),t.initCustomEvent("focus-visible-polyfill-ready",!1,!1,{})}window.dispatchEvent(t)}typeof document!="undefined"&&e(document)})});var cn=Pt(Er=>{(function(e){var t=function(){try{return!!Symbol.iterator}catch(c){return!1}},r=t(),n=function(c){var u={next:function(){var p=c.shift();return{done:p===void 0,value:p}}};return r&&(u[Symbol.iterator]=function(){return u}),u},o=function(c){return encodeURIComponent(c).replace(/%20/g,"+")},i=function(c){return decodeURIComponent(String(c).replace(/\+/g," "))},s=function(){var c=function(p){Object.defineProperty(this,"_entries",{writable:!0,value:{}});var m=typeof p;if(m!=="undefined")if(m==="string")p!==""&&this._fromString(p);else if(p instanceof c){var d=this;p.forEach(function(B,N){d.append(N,B)})}else if(p!==null&&m==="object")if(Object.prototype.toString.call(p)==="[object Array]")for(var h=0;hd[0]?1:0}),c._entries&&(c._entries={});for(var p=0;p1?i(d[1]):"")}})})(typeof global!="undefined"?global:typeof window!="undefined"?window:typeof self!="undefined"?self:Er);(function(e){var t=function(){try{var o=new e.URL("b","http://a");return o.pathname="c d",o.href==="http://a/c%20d"&&o.searchParams}catch(i){return!1}},r=function(){var o=e.URL,i=function(f,c){typeof f!="string"&&(f=String(f)),c&&typeof c!="string"&&(c=String(c));var u=document,p;if(c&&(e.location===void 0||c!==e.location.href)){c=c.toLowerCase(),u=document.implementation.createHTMLDocument(""),p=u.createElement("base"),p.href=c,u.head.appendChild(p);try{if(p.href.indexOf(c)!==0)throw new Error(p.href)}catch(O){throw new Error("URL unable to set base "+c+" due to "+O)}}var m=u.createElement("a");m.href=f,p&&(u.body.appendChild(m),m.href=m.href);var d=u.createElement("input");if(d.type="url",d.value=f,m.protocol===":"||!/:/.test(m.href)||!d.checkValidity()&&!c)throw new TypeError("Invalid URL");Object.defineProperty(this,"_anchorElement",{value:m});var h=new e.URLSearchParams(this.search),v=!0,Y=!0,B=this;["append","delete","set"].forEach(function(O){var Qe=h[O];h[O]=function(){Qe.apply(h,arguments),v&&(Y=!1,B.search=h.toString(),Y=!0)}}),Object.defineProperty(this,"searchParams",{value:h,enumerable:!0});var N=void 0;Object.defineProperty(this,"_updateSearchParams",{enumerable:!1,configurable:!1,writable:!1,value:function(){this.search!==N&&(N=this.search,Y&&(v=!1,this.searchParams._fromString(this.search),v=!0))}})},s=i.prototype,a=function(f){Object.defineProperty(s,f,{get:function(){return this._anchorElement[f]},set:function(c){this._anchorElement[f]=c},enumerable:!0})};["hash","host","hostname","port","protocol"].forEach(function(f){a(f)}),Object.defineProperty(s,"search",{get:function(){return this._anchorElement.search},set:function(f){this._anchorElement.search=f,this._updateSearchParams()},enumerable:!0}),Object.defineProperties(s,{toString:{get:function(){var f=this;return function(){return f.href}}},href:{get:function(){return this._anchorElement.href.replace(/\?$/,"")},set:function(f){this._anchorElement.href=f,this._updateSearchParams()},enumerable:!0},pathname:{get:function(){return this._anchorElement.pathname.replace(/(^\/?)/,"/")},set:function(f){this._anchorElement.pathname=f},enumerable:!0},origin:{get:function(){var f={"http:":80,"https:":443,"ftp:":21}[this._anchorElement.protocol],c=this._anchorElement.port!=f&&this._anchorElement.port!=="";return this._anchorElement.protocol+"//"+this._anchorElement.hostname+(c?":"+this._anchorElement.port:"")},enumerable:!0},password:{get:function(){return""},set:function(f){},enumerable:!0},username:{get:function(){return""},set:function(f){},enumerable:!0}}),i.createObjectURL=function(f){return o.createObjectURL.apply(o,arguments)},i.revokeObjectURL=function(f){return o.revokeObjectURL.apply(o,arguments)},e.URL=i};if(t()||r(),e.location!==void 0&&!("origin"in e.location)){var n=function(){return e.location.protocol+"//"+e.location.hostname+(e.location.port?":"+e.location.port:"")};try{Object.defineProperty(e.location,"origin",{get:n,enumerable:!0})}catch(o){setInterval(function(){e.location.origin=n()},100)}}})(typeof global!="undefined"?global:typeof window!="undefined"?window:typeof self!="undefined"?self:Er)});var qr=Pt((Mt,Nr)=>{/*! + * clipboard.js v2.0.11 + * https://clipboardjs.com/ + * + * Licensed MIT © Zeno Rocha + */(function(t,r){typeof Mt=="object"&&typeof Nr=="object"?Nr.exports=r():typeof define=="function"&&define.amd?define([],r):typeof Mt=="object"?Mt.ClipboardJS=r():t.ClipboardJS=r()})(Mt,function(){return function(){var e={686:function(n,o,i){"use strict";i.d(o,{default:function(){return Ai}});var s=i(279),a=i.n(s),f=i(370),c=i.n(f),u=i(817),p=i.n(u);function m(j){try{return document.execCommand(j)}catch(T){return!1}}var d=function(T){var E=p()(T);return m("cut"),E},h=d;function v(j){var T=document.documentElement.getAttribute("dir")==="rtl",E=document.createElement("textarea");E.style.fontSize="12pt",E.style.border="0",E.style.padding="0",E.style.margin="0",E.style.position="absolute",E.style[T?"right":"left"]="-9999px";var H=window.pageYOffset||document.documentElement.scrollTop;return E.style.top="".concat(H,"px"),E.setAttribute("readonly",""),E.value=j,E}var Y=function(T,E){var H=v(T);E.container.appendChild(H);var I=p()(H);return m("copy"),H.remove(),I},B=function(T){var E=arguments.length>1&&arguments[1]!==void 0?arguments[1]:{container:document.body},H="";return typeof T=="string"?H=Y(T,E):T instanceof HTMLInputElement&&!["text","search","url","tel","password"].includes(T==null?void 0:T.type)?H=Y(T.value,E):(H=p()(T),m("copy")),H},N=B;function O(j){"@babel/helpers - typeof";return typeof Symbol=="function"&&typeof Symbol.iterator=="symbol"?O=function(E){return typeof E}:O=function(E){return E&&typeof Symbol=="function"&&E.constructor===Symbol&&E!==Symbol.prototype?"symbol":typeof E},O(j)}var Qe=function(){var T=arguments.length>0&&arguments[0]!==void 0?arguments[0]:{},E=T.action,H=E===void 0?"copy":E,I=T.container,q=T.target,Me=T.text;if(H!=="copy"&&H!=="cut")throw new Error('Invalid "action" value, use either "copy" or "cut"');if(q!==void 0)if(q&&O(q)==="object"&&q.nodeType===1){if(H==="copy"&&q.hasAttribute("disabled"))throw new Error('Invalid "target" attribute. Please use "readonly" instead of "disabled" attribute');if(H==="cut"&&(q.hasAttribute("readonly")||q.hasAttribute("disabled")))throw new Error(`Invalid "target" attribute. You can't cut text from elements with "readonly" or "disabled" attributes`)}else throw new Error('Invalid "target" value, use a valid Element');if(Me)return N(Me,{container:I});if(q)return H==="cut"?h(q):N(q,{container:I})},De=Qe;function $e(j){"@babel/helpers - typeof";return typeof Symbol=="function"&&typeof Symbol.iterator=="symbol"?$e=function(E){return typeof E}:$e=function(E){return E&&typeof Symbol=="function"&&E.constructor===Symbol&&E!==Symbol.prototype?"symbol":typeof E},$e(j)}function Ei(j,T){if(!(j instanceof T))throw new TypeError("Cannot call a class as a function")}function tn(j,T){for(var E=0;E0&&arguments[0]!==void 0?arguments[0]:{};this.action=typeof I.action=="function"?I.action:this.defaultAction,this.target=typeof I.target=="function"?I.target:this.defaultTarget,this.text=typeof I.text=="function"?I.text:this.defaultText,this.container=$e(I.container)==="object"?I.container:document.body}},{key:"listenClick",value:function(I){var q=this;this.listener=c()(I,"click",function(Me){return q.onClick(Me)})}},{key:"onClick",value:function(I){var q=I.delegateTarget||I.currentTarget,Me=this.action(q)||"copy",kt=De({action:Me,container:this.container,target:this.target(q),text:this.text(q)});this.emit(kt?"success":"error",{action:Me,text:kt,trigger:q,clearSelection:function(){q&&q.focus(),window.getSelection().removeAllRanges()}})}},{key:"defaultAction",value:function(I){return vr("action",I)}},{key:"defaultTarget",value:function(I){var q=vr("target",I);if(q)return document.querySelector(q)}},{key:"defaultText",value:function(I){return vr("text",I)}},{key:"destroy",value:function(){this.listener.destroy()}}],[{key:"copy",value:function(I){var q=arguments.length>1&&arguments[1]!==void 0?arguments[1]:{container:document.body};return N(I,q)}},{key:"cut",value:function(I){return h(I)}},{key:"isSupported",value:function(){var I=arguments.length>0&&arguments[0]!==void 0?arguments[0]:["copy","cut"],q=typeof I=="string"?[I]:I,Me=!!document.queryCommandSupported;return q.forEach(function(kt){Me=Me&&!!document.queryCommandSupported(kt)}),Me}}]),E}(a()),Ai=Li},828:function(n){var o=9;if(typeof Element!="undefined"&&!Element.prototype.matches){var i=Element.prototype;i.matches=i.matchesSelector||i.mozMatchesSelector||i.msMatchesSelector||i.oMatchesSelector||i.webkitMatchesSelector}function s(a,f){for(;a&&a.nodeType!==o;){if(typeof a.matches=="function"&&a.matches(f))return a;a=a.parentNode}}n.exports=s},438:function(n,o,i){var s=i(828);function a(u,p,m,d,h){var v=c.apply(this,arguments);return u.addEventListener(m,v,h),{destroy:function(){u.removeEventListener(m,v,h)}}}function f(u,p,m,d,h){return typeof u.addEventListener=="function"?a.apply(null,arguments):typeof m=="function"?a.bind(null,document).apply(null,arguments):(typeof u=="string"&&(u=document.querySelectorAll(u)),Array.prototype.map.call(u,function(v){return a(v,p,m,d,h)}))}function c(u,p,m,d){return function(h){h.delegateTarget=s(h.target,p),h.delegateTarget&&d.call(u,h)}}n.exports=f},879:function(n,o){o.node=function(i){return i!==void 0&&i instanceof HTMLElement&&i.nodeType===1},o.nodeList=function(i){var s=Object.prototype.toString.call(i);return i!==void 0&&(s==="[object NodeList]"||s==="[object HTMLCollection]")&&"length"in i&&(i.length===0||o.node(i[0]))},o.string=function(i){return typeof i=="string"||i instanceof String},o.fn=function(i){var s=Object.prototype.toString.call(i);return s==="[object Function]"}},370:function(n,o,i){var s=i(879),a=i(438);function f(m,d,h){if(!m&&!d&&!h)throw new Error("Missing required arguments");if(!s.string(d))throw new TypeError("Second argument must be a String");if(!s.fn(h))throw new TypeError("Third argument must be a Function");if(s.node(m))return c(m,d,h);if(s.nodeList(m))return u(m,d,h);if(s.string(m))return p(m,d,h);throw new TypeError("First argument must be a String, HTMLElement, HTMLCollection, or NodeList")}function c(m,d,h){return m.addEventListener(d,h),{destroy:function(){m.removeEventListener(d,h)}}}function u(m,d,h){return Array.prototype.forEach.call(m,function(v){v.addEventListener(d,h)}),{destroy:function(){Array.prototype.forEach.call(m,function(v){v.removeEventListener(d,h)})}}}function p(m,d,h){return a(document.body,m,d,h)}n.exports=f},817:function(n){function o(i){var s;if(i.nodeName==="SELECT")i.focus(),s=i.value;else if(i.nodeName==="INPUT"||i.nodeName==="TEXTAREA"){var a=i.hasAttribute("readonly");a||i.setAttribute("readonly",""),i.select(),i.setSelectionRange(0,i.value.length),a||i.removeAttribute("readonly"),s=i.value}else{i.hasAttribute("contenteditable")&&i.focus();var f=window.getSelection(),c=document.createRange();c.selectNodeContents(i),f.removeAllRanges(),f.addRange(c),s=f.toString()}return s}n.exports=o},279:function(n){function o(){}o.prototype={on:function(i,s,a){var f=this.e||(this.e={});return(f[i]||(f[i]=[])).push({fn:s,ctx:a}),this},once:function(i,s,a){var f=this;function c(){f.off(i,c),s.apply(a,arguments)}return c._=s,this.on(i,c,a)},emit:function(i){var s=[].slice.call(arguments,1),a=((this.e||(this.e={}))[i]||[]).slice(),f=0,c=a.length;for(f;f{"use strict";/*! + * escape-html + * Copyright(c) 2012-2013 TJ Holowaychuk + * Copyright(c) 2015 Andreas Lubbe + * Copyright(c) 2015 Tiancheng "Timothy" Gu + * MIT Licensed + */var rs=/["'&<>]/;Yo.exports=ns;function ns(e){var t=""+e,r=rs.exec(t);if(!r)return t;var n,o="",i=0,s=0;for(i=r.index;i0&&i[i.length-1])&&(c[0]===6||c[0]===2)){r=0;continue}if(c[0]===3&&(!i||c[1]>i[0]&&c[1]=e.length&&(e=void 0),{value:e&&e[n++],done:!e}}};throw new TypeError(t?"Object is not iterable.":"Symbol.iterator is not defined.")}function W(e,t){var r=typeof Symbol=="function"&&e[Symbol.iterator];if(!r)return e;var n=r.call(e),o,i=[],s;try{for(;(t===void 0||t-- >0)&&!(o=n.next()).done;)i.push(o.value)}catch(a){s={error:a}}finally{try{o&&!o.done&&(r=n.return)&&r.call(n)}finally{if(s)throw s.error}}return i}function D(e,t,r){if(r||arguments.length===2)for(var n=0,o=t.length,i;n1||a(m,d)})})}function a(m,d){try{f(n[m](d))}catch(h){p(i[0][3],h)}}function f(m){m.value instanceof et?Promise.resolve(m.value.v).then(c,u):p(i[0][2],m)}function c(m){a("next",m)}function u(m){a("throw",m)}function p(m,d){m(d),i.shift(),i.length&&a(i[0][0],i[0][1])}}function pn(e){if(!Symbol.asyncIterator)throw new TypeError("Symbol.asyncIterator is not defined.");var t=e[Symbol.asyncIterator],r;return t?t.call(e):(e=typeof Ee=="function"?Ee(e):e[Symbol.iterator](),r={},n("next"),n("throw"),n("return"),r[Symbol.asyncIterator]=function(){return this},r);function n(i){r[i]=e[i]&&function(s){return new Promise(function(a,f){s=e[i](s),o(a,f,s.done,s.value)})}}function o(i,s,a,f){Promise.resolve(f).then(function(c){i({value:c,done:a})},s)}}function C(e){return typeof e=="function"}function at(e){var t=function(n){Error.call(n),n.stack=new Error().stack},r=e(t);return r.prototype=Object.create(Error.prototype),r.prototype.constructor=r,r}var It=at(function(e){return function(r){e(this),this.message=r?r.length+` errors occurred during unsubscription: +`+r.map(function(n,o){return o+1+") "+n.toString()}).join(` + `):"",this.name="UnsubscriptionError",this.errors=r}});function Ve(e,t){if(e){var r=e.indexOf(t);0<=r&&e.splice(r,1)}}var Ie=function(){function e(t){this.initialTeardown=t,this.closed=!1,this._parentage=null,this._finalizers=null}return e.prototype.unsubscribe=function(){var t,r,n,o,i;if(!this.closed){this.closed=!0;var s=this._parentage;if(s)if(this._parentage=null,Array.isArray(s))try{for(var a=Ee(s),f=a.next();!f.done;f=a.next()){var c=f.value;c.remove(this)}}catch(v){t={error:v}}finally{try{f&&!f.done&&(r=a.return)&&r.call(a)}finally{if(t)throw t.error}}else s.remove(this);var u=this.initialTeardown;if(C(u))try{u()}catch(v){i=v instanceof It?v.errors:[v]}var p=this._finalizers;if(p){this._finalizers=null;try{for(var m=Ee(p),d=m.next();!d.done;d=m.next()){var h=d.value;try{ln(h)}catch(v){i=i!=null?i:[],v instanceof It?i=D(D([],W(i)),W(v.errors)):i.push(v)}}}catch(v){n={error:v}}finally{try{d&&!d.done&&(o=m.return)&&o.call(m)}finally{if(n)throw n.error}}}if(i)throw new It(i)}},e.prototype.add=function(t){var r;if(t&&t!==this)if(this.closed)ln(t);else{if(t instanceof e){if(t.closed||t._hasParent(this))return;t._addParent(this)}(this._finalizers=(r=this._finalizers)!==null&&r!==void 0?r:[]).push(t)}},e.prototype._hasParent=function(t){var r=this._parentage;return r===t||Array.isArray(r)&&r.includes(t)},e.prototype._addParent=function(t){var r=this._parentage;this._parentage=Array.isArray(r)?(r.push(t),r):r?[r,t]:t},e.prototype._removeParent=function(t){var r=this._parentage;r===t?this._parentage=null:Array.isArray(r)&&Ve(r,t)},e.prototype.remove=function(t){var r=this._finalizers;r&&Ve(r,t),t instanceof e&&t._removeParent(this)},e.EMPTY=function(){var t=new e;return t.closed=!0,t}(),e}();var Sr=Ie.EMPTY;function jt(e){return e instanceof Ie||e&&"closed"in e&&C(e.remove)&&C(e.add)&&C(e.unsubscribe)}function ln(e){C(e)?e():e.unsubscribe()}var Le={onUnhandledError:null,onStoppedNotification:null,Promise:void 0,useDeprecatedSynchronousErrorHandling:!1,useDeprecatedNextContext:!1};var st={setTimeout:function(e,t){for(var r=[],n=2;n0},enumerable:!1,configurable:!0}),t.prototype._trySubscribe=function(r){return this._throwIfClosed(),e.prototype._trySubscribe.call(this,r)},t.prototype._subscribe=function(r){return this._throwIfClosed(),this._checkFinalizedStatuses(r),this._innerSubscribe(r)},t.prototype._innerSubscribe=function(r){var n=this,o=this,i=o.hasError,s=o.isStopped,a=o.observers;return i||s?Sr:(this.currentObservers=null,a.push(r),new Ie(function(){n.currentObservers=null,Ve(a,r)}))},t.prototype._checkFinalizedStatuses=function(r){var n=this,o=n.hasError,i=n.thrownError,s=n.isStopped;o?r.error(i):s&&r.complete()},t.prototype.asObservable=function(){var r=new F;return r.source=this,r},t.create=function(r,n){return new xn(r,n)},t}(F);var xn=function(e){ie(t,e);function t(r,n){var o=e.call(this)||this;return o.destination=r,o.source=n,o}return t.prototype.next=function(r){var n,o;(o=(n=this.destination)===null||n===void 0?void 0:n.next)===null||o===void 0||o.call(n,r)},t.prototype.error=function(r){var n,o;(o=(n=this.destination)===null||n===void 0?void 0:n.error)===null||o===void 0||o.call(n,r)},t.prototype.complete=function(){var r,n;(n=(r=this.destination)===null||r===void 0?void 0:r.complete)===null||n===void 0||n.call(r)},t.prototype._subscribe=function(r){var n,o;return(o=(n=this.source)===null||n===void 0?void 0:n.subscribe(r))!==null&&o!==void 0?o:Sr},t}(x);var Et={now:function(){return(Et.delegate||Date).now()},delegate:void 0};var wt=function(e){ie(t,e);function t(r,n,o){r===void 0&&(r=1/0),n===void 0&&(n=1/0),o===void 0&&(o=Et);var i=e.call(this)||this;return i._bufferSize=r,i._windowTime=n,i._timestampProvider=o,i._buffer=[],i._infiniteTimeWindow=!0,i._infiniteTimeWindow=n===1/0,i._bufferSize=Math.max(1,r),i._windowTime=Math.max(1,n),i}return t.prototype.next=function(r){var n=this,o=n.isStopped,i=n._buffer,s=n._infiniteTimeWindow,a=n._timestampProvider,f=n._windowTime;o||(i.push(r),!s&&i.push(a.now()+f)),this._trimBuffer(),e.prototype.next.call(this,r)},t.prototype._subscribe=function(r){this._throwIfClosed(),this._trimBuffer();for(var n=this._innerSubscribe(r),o=this,i=o._infiniteTimeWindow,s=o._buffer,a=s.slice(),f=0;f0?e.prototype.requestAsyncId.call(this,r,n,o):(r.actions.push(this),r._scheduled||(r._scheduled=ut.requestAnimationFrame(function(){return r.flush(void 0)})))},t.prototype.recycleAsyncId=function(r,n,o){var i;if(o===void 0&&(o=0),o!=null?o>0:this.delay>0)return e.prototype.recycleAsyncId.call(this,r,n,o);var s=r.actions;n!=null&&((i=s[s.length-1])===null||i===void 0?void 0:i.id)!==n&&(ut.cancelAnimationFrame(n),r._scheduled=void 0)},t}(Wt);var Sn=function(e){ie(t,e);function t(){return e!==null&&e.apply(this,arguments)||this}return t.prototype.flush=function(r){this._active=!0;var n=this._scheduled;this._scheduled=void 0;var o=this.actions,i;r=r||o.shift();do if(i=r.execute(r.state,r.delay))break;while((r=o[0])&&r.id===n&&o.shift());if(this._active=!1,i){for(;(r=o[0])&&r.id===n&&o.shift();)r.unsubscribe();throw i}},t}(Dt);var Oe=new Sn(wn);var M=new F(function(e){return e.complete()});function Vt(e){return e&&C(e.schedule)}function Cr(e){return e[e.length-1]}function Ye(e){return C(Cr(e))?e.pop():void 0}function Te(e){return Vt(Cr(e))?e.pop():void 0}function zt(e,t){return typeof Cr(e)=="number"?e.pop():t}var pt=function(e){return e&&typeof e.length=="number"&&typeof e!="function"};function Nt(e){return C(e==null?void 0:e.then)}function qt(e){return C(e[ft])}function Kt(e){return Symbol.asyncIterator&&C(e==null?void 0:e[Symbol.asyncIterator])}function Qt(e){return new TypeError("You provided "+(e!==null&&typeof e=="object"?"an invalid object":"'"+e+"'")+" where a stream was expected. You can provide an Observable, Promise, ReadableStream, Array, AsyncIterable, or Iterable.")}function zi(){return typeof Symbol!="function"||!Symbol.iterator?"@@iterator":Symbol.iterator}var Yt=zi();function Gt(e){return C(e==null?void 0:e[Yt])}function Bt(e){return un(this,arguments,function(){var r,n,o,i;return $t(this,function(s){switch(s.label){case 0:r=e.getReader(),s.label=1;case 1:s.trys.push([1,,9,10]),s.label=2;case 2:return[4,et(r.read())];case 3:return n=s.sent(),o=n.value,i=n.done,i?[4,et(void 0)]:[3,5];case 4:return[2,s.sent()];case 5:return[4,et(o)];case 6:return[4,s.sent()];case 7:return s.sent(),[3,2];case 8:return[3,10];case 9:return r.releaseLock(),[7];case 10:return[2]}})})}function Jt(e){return C(e==null?void 0:e.getReader)}function U(e){if(e instanceof F)return e;if(e!=null){if(qt(e))return Ni(e);if(pt(e))return qi(e);if(Nt(e))return Ki(e);if(Kt(e))return On(e);if(Gt(e))return Qi(e);if(Jt(e))return Yi(e)}throw Qt(e)}function Ni(e){return new F(function(t){var r=e[ft]();if(C(r.subscribe))return r.subscribe(t);throw new TypeError("Provided object does not correctly implement Symbol.observable")})}function qi(e){return new F(function(t){for(var r=0;r=2;return function(n){return n.pipe(e?A(function(o,i){return e(o,i,n)}):de,ge(1),r?He(t):Dn(function(){return new Zt}))}}function Vn(){for(var e=[],t=0;t=2,!0))}function pe(e){e===void 0&&(e={});var t=e.connector,r=t===void 0?function(){return new x}:t,n=e.resetOnError,o=n===void 0?!0:n,i=e.resetOnComplete,s=i===void 0?!0:i,a=e.resetOnRefCountZero,f=a===void 0?!0:a;return function(c){var u,p,m,d=0,h=!1,v=!1,Y=function(){p==null||p.unsubscribe(),p=void 0},B=function(){Y(),u=m=void 0,h=v=!1},N=function(){var O=u;B(),O==null||O.unsubscribe()};return y(function(O,Qe){d++,!v&&!h&&Y();var De=m=m!=null?m:r();Qe.add(function(){d--,d===0&&!v&&!h&&(p=$r(N,f))}),De.subscribe(Qe),!u&&d>0&&(u=new rt({next:function($e){return De.next($e)},error:function($e){v=!0,Y(),p=$r(B,o,$e),De.error($e)},complete:function(){h=!0,Y(),p=$r(B,s),De.complete()}}),U(O).subscribe(u))})(c)}}function $r(e,t){for(var r=[],n=2;ne.next(document)),e}function K(e,t=document){return Array.from(t.querySelectorAll(e))}function z(e,t=document){let r=ce(e,t);if(typeof r=="undefined")throw new ReferenceError(`Missing element: expected "${e}" to be present`);return r}function ce(e,t=document){return t.querySelector(e)||void 0}function _e(){return document.activeElement instanceof HTMLElement&&document.activeElement||void 0}function tr(e){return L(b(document.body,"focusin"),b(document.body,"focusout")).pipe(ke(1),l(()=>{let t=_e();return typeof t!="undefined"?e.contains(t):!1}),V(e===_e()),J())}function Xe(e){return{x:e.offsetLeft,y:e.offsetTop}}function Kn(e){return L(b(window,"load"),b(window,"resize")).pipe(Ce(0,Oe),l(()=>Xe(e)),V(Xe(e)))}function rr(e){return{x:e.scrollLeft,y:e.scrollTop}}function dt(e){return L(b(e,"scroll"),b(window,"resize")).pipe(Ce(0,Oe),l(()=>rr(e)),V(rr(e)))}var Yn=function(){if(typeof Map!="undefined")return Map;function e(t,r){var n=-1;return t.some(function(o,i){return o[0]===r?(n=i,!0):!1}),n}return function(){function t(){this.__entries__=[]}return Object.defineProperty(t.prototype,"size",{get:function(){return this.__entries__.length},enumerable:!0,configurable:!0}),t.prototype.get=function(r){var n=e(this.__entries__,r),o=this.__entries__[n];return o&&o[1]},t.prototype.set=function(r,n){var o=e(this.__entries__,r);~o?this.__entries__[o][1]=n:this.__entries__.push([r,n])},t.prototype.delete=function(r){var n=this.__entries__,o=e(n,r);~o&&n.splice(o,1)},t.prototype.has=function(r){return!!~e(this.__entries__,r)},t.prototype.clear=function(){this.__entries__.splice(0)},t.prototype.forEach=function(r,n){n===void 0&&(n=null);for(var o=0,i=this.__entries__;o0},e.prototype.connect_=function(){!Wr||this.connected_||(document.addEventListener("transitionend",this.onTransitionEnd_),window.addEventListener("resize",this.refresh),va?(this.mutationsObserver_=new MutationObserver(this.refresh),this.mutationsObserver_.observe(document,{attributes:!0,childList:!0,characterData:!0,subtree:!0})):(document.addEventListener("DOMSubtreeModified",this.refresh),this.mutationEventsAdded_=!0),this.connected_=!0)},e.prototype.disconnect_=function(){!Wr||!this.connected_||(document.removeEventListener("transitionend",this.onTransitionEnd_),window.removeEventListener("resize",this.refresh),this.mutationsObserver_&&this.mutationsObserver_.disconnect(),this.mutationEventsAdded_&&document.removeEventListener("DOMSubtreeModified",this.refresh),this.mutationsObserver_=null,this.mutationEventsAdded_=!1,this.connected_=!1)},e.prototype.onTransitionEnd_=function(t){var r=t.propertyName,n=r===void 0?"":r,o=ba.some(function(i){return!!~n.indexOf(i)});o&&this.refresh()},e.getInstance=function(){return this.instance_||(this.instance_=new e),this.instance_},e.instance_=null,e}(),Gn=function(e,t){for(var r=0,n=Object.keys(t);r0},e}(),Jn=typeof WeakMap!="undefined"?new WeakMap:new Yn,Xn=function(){function e(t){if(!(this instanceof e))throw new TypeError("Cannot call a class as a function.");if(!arguments.length)throw new TypeError("1 argument required, but only 0 present.");var r=ga.getInstance(),n=new La(t,r,this);Jn.set(this,n)}return e}();["observe","unobserve","disconnect"].forEach(function(e){Xn.prototype[e]=function(){var t;return(t=Jn.get(this))[e].apply(t,arguments)}});var Aa=function(){return typeof nr.ResizeObserver!="undefined"?nr.ResizeObserver:Xn}(),Zn=Aa;var eo=new x,Ca=$(()=>k(new Zn(e=>{for(let t of e)eo.next(t)}))).pipe(g(e=>L(ze,k(e)).pipe(R(()=>e.disconnect()))),X(1));function he(e){return{width:e.offsetWidth,height:e.offsetHeight}}function ye(e){return Ca.pipe(S(t=>t.observe(e)),g(t=>eo.pipe(A(({target:r})=>r===e),R(()=>t.unobserve(e)),l(()=>he(e)))),V(he(e)))}function bt(e){return{width:e.scrollWidth,height:e.scrollHeight}}function ar(e){let t=e.parentElement;for(;t&&(e.scrollWidth<=t.scrollWidth&&e.scrollHeight<=t.scrollHeight);)t=(e=t).parentElement;return t?e:void 0}var to=new x,Ra=$(()=>k(new IntersectionObserver(e=>{for(let t of e)to.next(t)},{threshold:0}))).pipe(g(e=>L(ze,k(e)).pipe(R(()=>e.disconnect()))),X(1));function sr(e){return Ra.pipe(S(t=>t.observe(e)),g(t=>to.pipe(A(({target:r})=>r===e),R(()=>t.unobserve(e)),l(({isIntersecting:r})=>r))))}function ro(e,t=16){return dt(e).pipe(l(({y:r})=>{let n=he(e),o=bt(e);return r>=o.height-n.height-t}),J())}var cr={drawer:z("[data-md-toggle=drawer]"),search:z("[data-md-toggle=search]")};function no(e){return cr[e].checked}function Ke(e,t){cr[e].checked!==t&&cr[e].click()}function Ue(e){let t=cr[e];return b(t,"change").pipe(l(()=>t.checked),V(t.checked))}function ka(e,t){switch(e.constructor){case HTMLInputElement:return e.type==="radio"?/^Arrow/.test(t):!0;case HTMLSelectElement:case HTMLTextAreaElement:return!0;default:return e.isContentEditable}}function Ha(){return L(b(window,"compositionstart").pipe(l(()=>!0)),b(window,"compositionend").pipe(l(()=>!1))).pipe(V(!1))}function oo(){let e=b(window,"keydown").pipe(A(t=>!(t.metaKey||t.ctrlKey)),l(t=>({mode:no("search")?"search":"global",type:t.key,claim(){t.preventDefault(),t.stopPropagation()}})),A(({mode:t,type:r})=>{if(t==="global"){let n=_e();if(typeof n!="undefined")return!ka(n,r)}return!0}),pe());return Ha().pipe(g(t=>t?M:e))}function le(){return new URL(location.href)}function ot(e){location.href=e.href}function io(){return new x}function ao(e,t){if(typeof t=="string"||typeof t=="number")e.innerHTML+=t.toString();else if(t instanceof Node)e.appendChild(t);else if(Array.isArray(t))for(let r of t)ao(e,r)}function _(e,t,...r){let n=document.createElement(e);if(t)for(let o of Object.keys(t))typeof t[o]!="undefined"&&(typeof t[o]!="boolean"?n.setAttribute(o,t[o]):n.setAttribute(o,""));for(let o of r)ao(n,o);return n}function fr(e){if(e>999){let t=+((e-950)%1e3>99);return`${((e+1e-6)/1e3).toFixed(t)}k`}else return e.toString()}function so(){return location.hash.substring(1)}function Dr(e){let t=_("a",{href:e});t.addEventListener("click",r=>r.stopPropagation()),t.click()}function Pa(e){return L(b(window,"hashchange"),e).pipe(l(so),V(so()),A(t=>t.length>0),X(1))}function co(e){return Pa(e).pipe(l(t=>ce(`[id="${t}"]`)),A(t=>typeof t!="undefined"))}function Vr(e){let t=matchMedia(e);return er(r=>t.addListener(()=>r(t.matches))).pipe(V(t.matches))}function fo(){let e=matchMedia("print");return L(b(window,"beforeprint").pipe(l(()=>!0)),b(window,"afterprint").pipe(l(()=>!1))).pipe(V(e.matches))}function zr(e,t){return e.pipe(g(r=>r?t():M))}function ur(e,t={credentials:"same-origin"}){return ue(fetch(`${e}`,t)).pipe(fe(()=>M),g(r=>r.status!==200?Ot(()=>new Error(r.statusText)):k(r)))}function We(e,t){return ur(e,t).pipe(g(r=>r.json()),X(1))}function uo(e,t){let r=new DOMParser;return ur(e,t).pipe(g(n=>n.text()),l(n=>r.parseFromString(n,"text/xml")),X(1))}function pr(e){let t=_("script",{src:e});return $(()=>(document.head.appendChild(t),L(b(t,"load"),b(t,"error").pipe(g(()=>Ot(()=>new ReferenceError(`Invalid script: ${e}`))))).pipe(l(()=>{}),R(()=>document.head.removeChild(t)),ge(1))))}function po(){return{x:Math.max(0,scrollX),y:Math.max(0,scrollY)}}function lo(){return L(b(window,"scroll",{passive:!0}),b(window,"resize",{passive:!0})).pipe(l(po),V(po()))}function mo(){return{width:innerWidth,height:innerHeight}}function ho(){return b(window,"resize",{passive:!0}).pipe(l(mo),V(mo()))}function bo(){return G([lo(),ho()]).pipe(l(([e,t])=>({offset:e,size:t})),X(1))}function lr(e,{viewport$:t,header$:r}){let n=t.pipe(ee("size")),o=G([n,r]).pipe(l(()=>Xe(e)));return G([r,t,o]).pipe(l(([{height:i},{offset:s,size:a},{x:f,y:c}])=>({offset:{x:s.x-f,y:s.y-c+i},size:a})))}(()=>{function e(n,o){parent.postMessage(n,o||"*")}function t(...n){return n.reduce((o,i)=>o.then(()=>new Promise(s=>{let a=document.createElement("script");a.src=i,a.onload=s,document.body.appendChild(a)})),Promise.resolve())}var r=class extends EventTarget{constructor(n){super(),this.url=n,this.m=i=>{i.source===this.w&&(this.dispatchEvent(new MessageEvent("message",{data:i.data})),this.onmessage&&this.onmessage(i))},this.e=(i,s,a,f,c)=>{if(s===`${this.url}`){let u=new ErrorEvent("error",{message:i,filename:s,lineno:a,colno:f,error:c});this.dispatchEvent(u),this.onerror&&this.onerror(u)}};let o=document.createElement("iframe");o.hidden=!0,document.body.appendChild(this.iframe=o),this.w.document.open(),this.w.document.write(` + + + + + + + + + + + + + + + + + + + + + + + + + +

+
+ +
+ + + + + + +
+ + +
+ +
+ + + + + + + + + +
+
+ + + +
+
+
+ + + + + + + + +
+
+
+ + + + +
+
+ + + + +

Synthesize tabular data

+

Use YData's RegularSynthesizer to generate tabular synthetic data

+
import os
+
+from ydata.sdk.dataset import get_dataset
+from ydata.sdk.synthesizers import RegularSynthesizer
+
+# Do not forget to add your token as env variables
+os.environ["YDATA_TOKEN"] = '<TOKEN>'  # Remove if already defined
+
+
+def main():
+    """In this example, we demonstrate how to train a synthesizer from a pandas
+    DataFrame.
+
+    After training a Regular Synthesizer, we request a sample.
+    """
+    X = get_dataset('census')
+
+    # We initialize a regular synthesizer
+    # As long as the synthesizer does not call `fit`, it exists only locally
+    synth = RegularSynthesizer()
+
+    # We train the synthesizer on our dataset
+    synth.fit(X)
+
+    # We request a synthetic dataset with 50 rows
+    sample = synth.sample(n_samples=50)
+
+    print(sample.shape)
+
+
+if __name__ == "__main__":
+    main()
+
+ + + + + + +
+
+ + + + +
+ + + +
+ + + +
+
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/0.7/examples/synthesize_timeseries_data/index.html b/0.7/examples/synthesize_timeseries_data/index.html new file mode 100644 index 00000000..a25effa1 --- /dev/null +++ b/0.7/examples/synthesize_timeseries_data/index.html @@ -0,0 +1,1287 @@ + + + + + + + + + + + + + + + + + + + + + + Generate Time-Series Data - YData Fabric + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ + + + + + +
+ + +
+ +
+ + + + + + + + + +
+
+ + + +
+
+
+ + + + + + + + +
+
+
+ + + + +
+
+ + + + +

Synthesize time-series data

+

Use YData's TimeSeriesSynthesizer to generate time-series synthetic data

+

Tabular data is the most common type of data we encounter in data problems.

+

When thinking about tabular data, we assume independence between different records, but this does not happen in reality. Suppose we check events from our day-to-day life, such as room temperature changes, bank account transactions, stock price fluctuations, and air quality measurements in our neighborhood. In that case, we might end up with datasets where measures and records evolve and are related through time. This type of data is known to be sequential or time-series data.

+

Thus, sequential or time-series data refers to any data containing elements ordered into sequences in a structured format. +Dissecting any time-series dataset, we see differences in variables' behavior that need to be understood for an effective generation of synthetic data. Typically any time-series dataset is composed of the following:

+
    +
  • Variables that define the order of time (these can be simple with one variable or composed)
  • +
  • Time-variant variables
  • +
  • Variables that refer to entities (single or multiple entities)
  • +
  • Variables that are attributes (those that don't depend on time but rather on the entity)
  • +
+

Below find an example:

+
import os
+
+from ydata.sdk.dataset import get_dataset
+from ydata.sdk.synthesizers import TimeSeriesSynthesizer
+
+# Do not forget to add your token as env variable
+os.environ["YDATA_TOKEN"] = '<TOKEN>'
+
+X = get_dataset('occupancy')
+
+# We initialize a time series synthesizer
+# As long as the synthesizer does not call `fit`, it exists only locally
+synth = TimeSeriesSynthesizer()
+
+# We train the synthesizer on our dataset
+# sortbykey -> variable that define the time order for the sequence
+synth.fit(X, sortbykey='date')
+
+# By default it is requested a synthetic sample with the same length as the original data
+# The TimeSeriesSynthesizer is designed to replicate temporal series and therefore the original time-horizon is respected
+sample = synth.sample(n_entities=1)
+
+ + + + + + +
+
+ + + + +
+ + + +
+ + + +
+
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/0.7/examples/synthesize_with_anonymization/index.html b/0.7/examples/synthesize_with_anonymization/index.html new file mode 100644 index 00000000..4818f5de --- /dev/null +++ b/0.7/examples/synthesize_with_anonymization/index.html @@ -0,0 +1,1316 @@ + + + + + + + + + + + + + + + + + + + + + + Anonymization - YData Fabric + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ + + + + + +
+ + +
+ +
+ + + + + + + + + +
+
+ + + +
+
+
+ + + + + + + + +
+
+
+ + + + +
+
+ + + + +

Anonymization

+

YData Synthesizers offers a way to anonymize sensitive information such that the original values are not present in the synthetic data but replaced by fake values.

+
+

Does the model retain the original values?

+

No! The anonymization is performed before the model training such that it never sees the original values.

+
+

The anonymization is performed by specifying which columns need to be anonymized and how to perform the anonymization. +The anonymization rules are defined as a dictionary with the following format:

+

{column_name: anonymization_rule}

+

While here are some predefined anonymization rules such as name, email, company, it is also possible to create a rule using a regular expression. +The anonymization rules have to be passed to a synthesizer in its fit method using the parameter anonymize.

+
+

What is the difference between anonymization and privacy?

+

Anonymization makes sure sensitive information are hidden from the data. +Privacy makes sure it is not possible to infer the original data points from the synthetic data points via statistical attacks.

+

Therefore, for data sharing anonymization and privacy controls are complementary.

+
+

The example below demonstrates how to anonymize the column Name by fake names and the column Ticket by a regular expression: +

import os
+
+from ydata.sdk.dataset import get_dataset
+from ydata.sdk.synthesizers import RegularSynthesizer
+
+# Do not forget to add your token as env variables
+os.environ["YDATA_TOKEN"] = '<TOKEN>'  # Remove if already defined
+
+
+def main():
+    """In this example, we demonstrate how to train a synthesizer from a pandas
+    DataFrame.
+
+    After training a Regular Synthesizer, we request a sample.
+    """
+    X = get_dataset('titanic')
+
+    # We initialize a regular synthesizer
+    # As long as the synthesizer does not call `fit`, it exists only locally
+    synth = RegularSynthesizer()
+
+    # We define anonymization rules, which is a dictionary with format:
+    # {column_name: anonymization_rule, ...}
+    # while here are some predefined anonymization rules like: name, email, company
+    # it is also possible to create a rule using a regular expression
+    rules = {
+        "Name": "name",
+        "Ticket": "[A-Z]{2}-[A-Z]{4}"
+    }
+
+    # We train the synthesizer on our dataset
+    synth.fit(
+        X,
+        name="titanic_synthesizer",
+        anonymize=rules
+    )
+
+    # We request a synthetic dataset with 50 rows
+    sample = synth.sample(n_samples=50)
+
+    print(sample[["Name", "Ticket"]].head(3))
+
+
+if __name__ == "__main__":
+    main()
+

+ + + + + + +
+
+ + + + +
+ + + +
+ + + +
+
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/0.7/examples/synthesize_with_conditional_sampling/index.html b/0.7/examples/synthesize_with_conditional_sampling/index.html new file mode 100644 index 00000000..ea62d561 --- /dev/null +++ b/0.7/examples/synthesize_with_conditional_sampling/index.html @@ -0,0 +1,1308 @@ + + + + + + + + + + + + + + + + + + + + + + Conditional Sampling - YData Fabric + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ + + + + + +
+ + +
+ +
+ + + + + + + + + +
+
+ + + +
+
+
+ + + + + + + + +
+
+
+ + + + +
+
+ + + + +

Conditional sampling

+

YData Synthesizers support conditional sampling. The fit method has an optional parameter named condition_on, which receives a list of features to condition upon. Furthermore, the sample method receives the conditions to be applied through another optional parameter also named condition_on. For now, two types of conditions are supported:

+
    +
  • Condition upon a categorical (or string) feature. The parameters are the name of the feature and a list of values (i.e., categories) to be considered. Each category also has its percentage of representativeness. For example, if we want to condition upon two categories, we need to define the percentage of rows each of these categories will have on the synthetic dataset. Naturally, the sum of such percentages needs to be 1. The default percentage is also 1 since it is the required value for a single category.
  • +
  • Condition upon a numerical feature. The parameters are the name of the feature and the minimum and maximum of the range to be considered. This feature will present a uniform distribution on the synthetic dataset, limited by the specified range.
  • +
+

The example below demonstrates how to train and sample from a synthesizer using conditional sampling:

+
import os
+
+from ydata.sdk.dataset import get_dataset
+from ydata.sdk.synthesizers import RegularSynthesizer
+
+# Do not forget to add your token as env variables.
+os.environ["YDATA_TOKEN"] = '<TOKEN>'  # Remove if already defined.
+
+
+def main():
+    """In this example, we demonstrate how to train and
+    sample from a synthesizer using conditional sampling."""
+    X = get_dataset('census')
+
+    # We initialize a regular synthesizer.
+    # As long as the synthesizer does not call `fit`, it exists only locally.
+    synth = RegularSynthesizer()
+
+    # We train the synthesizer on our dataset setting
+    # the features to condition upon.
+    synth.fit(
+        X,
+        name="census_synthesizer",
+        condition_on=["sex", "native-country", "age"]
+    )
+
+    # We request a synthetic dataset with specific condition rules.
+    sample = synth.sample(
+        n_samples=500,
+        condition_on={
+            "sex": {
+                "categories": ["Female"]
+            },
+            "native-country": {
+                "categories": [("United-States", 0.6),
+                               ("Mexico", 0.4)]
+            },
+            "age": {
+                "minimum": 55,
+                "maximum": 60
+            }
+        }
+    )
+    print(sample)
+
+
+if __name__ == "__main__":
+    main()
+
+ + + + + + +
+
+ + + + +
+ + + +
+ + + +
+
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/0.7/examples/synthesize_with_privacy_control/index.html b/0.7/examples/synthesize_with_privacy_control/index.html new file mode 100644 index 00000000..3494f43b --- /dev/null +++ b/0.7/examples/synthesize_with_privacy_control/index.html @@ -0,0 +1,1303 @@ + + + + + + + + + + + + + + + + + + + + + + Privacy Level - YData Fabric + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ + + + + + +
+ + +
+ +
+ + + + + + + + + +
+
+ + + +
+
+
+ + + + + + + + +
+
+
+ + + + +
+
+ + + + +

Privacy control

+

YData Synthesizers offers 3 different levels of privacy:

+
    +
  1. high privacy: the model is optimized for privacy purposes,
  2. +
  3. high fidelity (default): the model is optimized for high fidelity,
  4. +
  5. balanced: tradeoff between privacy and fidelity.
  6. +
+

The default privacy level is high fidelity. The privacy level can be changed by the user at the moment a synthesizer level is trained by using the parameter privacy_level. +The parameter expect a PrivacyLevel value.

+
+

What is the difference between anonymization and privacy?

+

Anonymization makes sure sensitive information are hidden from the data. +Privacy makes sure it is not possible to infer the original data points from the synthetic data points via statistical attacks.

+

Therefore, for data sharing anonymization and privacy controls are complementary.

+
+

The example below demonstrates how to train a synthesizer configured for high privacy:

+
import os
+
+from ydata.sdk.dataset import get_dataset
+from ydata.sdk.synthesizers import PrivacyLevel, RegularSynthesizer
+
+# Do not forget to add your token as env variables
+os.environ["YDATA_TOKEN"] = '<TOKEN>'  # Remove if already defined
+
+
+def main():
+    """In this example, we demonstrate how to train a synthesizer
+    with a high-privacy setting from a pandas DataFrame.
+    After training a Regular Synthesizer, we request a sample.
+    """
+    X = get_dataset('titanic')
+
+    # We initialize a regular synthesizer
+    # As long as the synthesizer does not call `fit`, it exists only locally
+    synth = RegularSynthesizer()
+
+    # We train the synthesizer on our dataset setting the privacy level to high
+    synth.fit(
+        X,
+        name="titanic_synthesizer",
+        privacy_level=PrivacyLevel.HIGH_PRIVACY
+    )
+
+    # We request a synthetic dataset with 50 rows
+    sample = synth.sample(n_samples=50)
+    print(sample)
+
+
+if __name__ == "__main__":
+    main()
+
+ + + + + + +
+
+ + + + +
+ + + +
+ + + +
+
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/0.7/get-started/create_lab/index.html b/0.7/get-started/create_lab/index.html new file mode 100644 index 00000000..4e099ea8 --- /dev/null +++ b/0.7/get-started/create_lab/index.html @@ -0,0 +1,1303 @@ + + + + + + + + + + + + + + + + + + + + + + How to create your first Lab - YData Fabric + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ + + + + + +
+ + +
+ +
+ + + + + + + + + +
+
+ + + +
+
+
+ + + + + + + + +
+
+
+ + + + +
+
+ + + + +

How to create your first Lab environment

+

Labs are code environments for a more flexible development of data-driven solutions while leveraging Fabric capabilities +combined with already loved tools such as scikit-learn, numpy and pandas. +To create your first Lab, you can use the “Create Lab” from Fabric’s home, or you can access it from the Labs +module by selecting it on the left side menu, and clicking the “Create Lab” button.

+

Select create a lab from Home

+

Next, a menu with different IDEs will be shown. As a quickstart select Jupyter Lab. As labs are development environments +you will be also asked what language you would prefer your environment to support: R or Python. Select Python.

+ + + + + + + + + + + + + +
Select IDESelect language
Select an IDEPython or R
+

Bundles are environments with pre-installed packages. Select YData bundle, so we can leverage some other Fabric features +such as Data Profiling, Synthetic Data and Pipelines.

+

Select the bundle for development

+

As a last step, you will be asked to configure the infrastructure resources for this new environment as well as giving it +a Display Name. We will keep the defaults, +but you have flexibility to select GPU acceleration or whether you need more computational resources for your developments.

+

Select the computational resources

+

Finally, your Lab will be created and added to the "Labs" list, as per the image below. The status of the lab will be +🟡 while preparing, and this process takes a few minutes, as the infrastructure is being allocated to your development environment. +As soon as the status changes to 🟢, you can open your lab by clicking in the button as shown below:

+

Open your lab environment

+

Create a new notebook in the JupyterLab and give it a name. You are now ready to start your developments!

+ + + + + + + + + + + + + +
Create a new notebookNotebook created
Create a new notebookNotebook created
+

Congrats! 🚀 You have now successfully created your first Lab a code environment, so you can benefit from the most +advanced Fabric features as well as compose complex data workflows. +Get ready for your journey of improved quality data for AI.

+ + + + + + +
+
+ + + + +
+ + + +
+ + + +
+
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/0.7/get-started/create_pipeline/index.html b/0.7/get-started/create_pipeline/index.html new file mode 100644 index 00000000..3b36d48b --- /dev/null +++ b/0.7/get-started/create_pipeline/index.html @@ -0,0 +1,1340 @@ + + + + + + + + + + + + + + + + + + + + + + How to create your first Pipeline - YData Fabric + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ + + + + + +
+ + +
+ +
+ + + + + + + + + +
+
+ + + +
+
+
+ + + + + + + + +
+
+
+ + + + +
+
+ + + + +

How to create your first Pipeline

+

+Check this quickstart video on how to create your first Pipeline.

+

The best way to get started with Pipelines is to use the interactive Pipeline editor available in the Labs with Jupyter Lab set as IDE. +If you don't have a Lab yet, or you don't know how to create one, check our quickstart guide on how to create your first lab.

+

Open an already existing lab.

+

A Pipeline comprises one or more nodes that are connected (or not!) with each other to define execution dependencies. Each pipeline node +is and should be implemented as a component that is expected to manage a single task, such as read the data, profiling the data, training a model, +or even publishing a model to production environments.

+

In this tutorial we will build a simple and generic pipeline that use a Dataset from Fabric's Data Catalog and profile to check it's quality. +We have the notebooks template already available. For that you need to access the "Academy" folder as per the image below.

+

Academy folder

+

Make sure to copy all the files in the folder "3 - Pipelines/quickstart" to the root folder of your lab, as per the image below.

+

Select your pipeline editor

+

Now that we have our notebooks we need to make a small change in the notebook "1. Read dataset". Go back to your Data Catalog, from one of the datasets +in your Catalog list, select the three vertical dots and click in "Explore in Labs" as shown in the image below.

+

Explore the dataset in the labs

+

The following screen will be shown. Click in copy.

+

Dataset code snippet

+

Now that we have copied the code, let's get back to our "1. Read data.ipynb" notebook, and replace the first code cell by with the new code. This will allow us to use a +dataset from the Data Catalog in our pipeline.

+ + + + + + + + + + + + + +
Placeholder codeReplaced with code snippet
Select an IDEPython or R
+

With our notebooks ready, we can now configure our Pipeline. +For this quickstart we will be leveraging an already existing pipeline - double-click the file my_first_pipeline.pipeline. You should see a pipeline +as depicted in the images below. +To create a new Pipeline, you can open the lab launcher tab and select "Pipeline Editor".

+ + + + + + + + + + + + + +
Open PipelineMy first pipeline
Open pipelinePython or R
+

Before running the pipeline, we need to check each component/step properties and configurations. Right-click each one of the steps, select "Open Properties", and a +menu will be depicted in your right side. Make sure that you have "YData - CPU" selected as the Runtime Image as show below.

+ + + + + + + + + + + + + +
Open propertiesRuntime image
Open pipelinePython or R
+

We are now ready to create and run our first pipeline. In the top left corner of the pipeline editor, the run button +will be available for you to click.

+

Select your pipeline editor

+

Accept the default values shown in the run dialog and start the run

+

Pipeline configuration confirm dialog

+

If the following message is shown, it means that you have create a run of your first pipeline.

+

Select your pipeline editor

+

Now that you have created your first pipeline, you can select the Pipeline from Fabric's left side menu.

+

Select Fabric Pipelines

+

Your most recent pipeline will be listed, as shown in below image.

+

My first pipeline listed

+

To check the run of your pipeline, jump into the "Run" tab. You will be able to see your first pipeline running!

+

Run pipeline

+

By clicking on top of the record you will be able to see the progress of the run step-by-step, and visualize the outputs of each and every +step by clicking on each step and selecting the Visualizations tab.

+

Run pipeline

+

Congrats! 🚀 You have now successfully created your first Pipeline a code environment, so you can benefit from Fabric's +orchestration engine to crate scalable, versionable and comparable data workflows. +Get ready for your journey of improved quality data for AI.

+ + + + + + +
+
+ + + + +
+ + + +
+ + + +
+
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/0.7/get-started/create_syntheticdata_generator/index.html b/0.7/get-started/create_syntheticdata_generator/index.html new file mode 100644 index 00000000..cffbcdb8 --- /dev/null +++ b/0.7/get-started/create_syntheticdata_generator/index.html @@ -0,0 +1,1290 @@ + + + + + + + + + + + + + + + + + + + + + + How to create your first Synthetic Data generator - YData Fabric + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ + + + + + +
+ + +
+ +
+ + + + + + + + + +
+
+ + + +
+
+
+ + + + + + + + +
+
+
+ + + + +
+
+ + + + +

How to create your first Synthetic Data generator

+

+Check this quickstart video on how to create your first Synthetic Data generator.

+

To generate your first synthetic data, you need to have a Dataset already available in your Data Catalog. +Check this tutorial to see how you can add your first dataset to Fabric’s Data Catalog.

+

With your first dataset created, you are now able to start the creation of your Synthetic Data generator. You can either +select "Synthetic Data" from your left side menu, or you can select "Create Synthetic Data" in your project Home +as shown in the image below.

+

Create Synthetic Data

+

You'll be asked to select the dataset you wish to generate synthetic data from and verify the columns you'd like to +include in the synthesis process, validating their Variable and Data Types.

+
+

Data types are relevant for synthetic data quality

+

Data Types are important to be revisited and aligned with the objectives for the synthetic data as they can highly impact the quality +of the generated data. For example, let's say we have a column that is a "Name", while is some situations it would make sense +to consider it a String, under the light of a dataset where "Name" refers to the name of the product purchases, it might be more +beneficial to set it as a Category.

+
+

Configure Metadata

+

Finally, as the last step of our process it comes the Synthetic Data specific configurations, for this particular case we +only need to define a Display Name, and we can finish the process by clicking in the "Save" button as per the image below.

+

Save Synthetic Data configurations

+

Your Synthetic Data generator is now training and listed under "Synthetic Data". While the model is being trained, the Status will be +🟡, as soon as the training is completed successfully it will transition to 🟢 as per the image below.

+

Synthetic data generator trained successfully

+

Once the Synthetic Data generator has finished training, you're ready to start generating your first synthetic dataset. +You can start by exploring an overview of the model configurations and even download a PDF report with a comprehensive overview of your +Synthetic Data Quality Metrics. Next, you can generate synthetic data samples by accessing the Generation tab or click on "Go to Generation".

+

Synthetic data generator overview

+

In this section, you are able to generate as many synthetic samples as you want. +For that you need to define the number rows to generate and click "Generate", as depicted in the image below.

+

Generate synthetic data records

+

A new line in your "Sample History" will be shown and as soon as the sample generation is completed you will be able to +"Compare" your synthetic data with the original data, add as a Dataset with "Add to Data Catalog" and last but not the least +download it as a file with "Download csv".

+

Synthetic data generator trained

+

Congrats! 🚀 You have now successfully created your first Synthetic Data generator with Fabric. +Get ready for your journey of improved quality data for AI.

+ + + + + + +
+
+ + + + +
+ + + +
+ + + +
+
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/0.7/get-started/fabric_community/index.html b/0.7/get-started/fabric_community/index.html new file mode 100644 index 00000000..0d8240d6 --- /dev/null +++ b/0.7/get-started/fabric_community/index.html @@ -0,0 +1,1300 @@ + + + + + + + + + + + + + + + + + + + + + + Fabric Community - YData Fabric + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ + + + + + +
+ + +
+ +
+ + + + + + + + + +
+
+ + + +
+
+
+ + + + + + + + +
+
+
+ + + + +
+
+ + + + +

Get started with Fabric Community

+

Fabric Community is a SaaS version that allows you to explore all the functionalities of Fabric first-hand: free, forever, for everyone. You’ll be able to validate your data quality with automated profiling, unlock data sharing and improve your ML models with synthetic data, and increase your productivity with seamless integration:

+
    +
  • Build 1 personal project;
  • +
  • Create your first Data Catalog and benefit from automated data profiling;
  • +
  • Train and generate synthetic data up to 2 models and datasets with 50 columns and 100K rows;
  • +
  • Optimize synthetic data quality for your use cases with an evaluation PDF report;
  • +
  • Create 1 development environment (Labs) and integrate it with your familiar ML packages and workflows.
  • +
+

Register

+

To register for Fabric Community:

+ +

Registration Process

+

Once you login, you'll access the Home page and get started with your data preparation!

+

Welcome Screen

+ + + + + + +
+
+ + + + +
+ + + +
+ + + +
+
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/0.7/get-started/index.html b/0.7/get-started/index.html new file mode 100644 index 00000000..fa4462a6 --- /dev/null +++ b/0.7/get-started/index.html @@ -0,0 +1,1248 @@ + + + + + + + + + + + + + + + + + + + + + + Get started with Fabric - YData Fabric + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ + + + + + +
+ + +
+ +
+ + + + + + + + + +
+
+ + + +
+
+
+ + + + + + + + +
+
+
+ + + + +
+ +
+ + + + +
+ + + +
+ + + +
+
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/0.7/get-started/upload_csv/index.html b/0.7/get-started/upload_csv/index.html new file mode 100644 index 00000000..eb2564ac --- /dev/null +++ b/0.7/get-started/upload_csv/index.html @@ -0,0 +1,1295 @@ + + + + + + + + + + + + + + + + + + + + + + How to create your first Dataset from a CSV file - YData Fabric + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ + + + + + +
+ + +
+ +
+ + + + + + + + + +
+
+ + + +
+
+
+ + + + + + + + +
+
+
+ + + + +
+
+ + + + +

How to create your first Dataset from a CSV file

+

+Check this quickstart video on how to create your first Dataset from a CSV file.

+

To create your first dataset in the Data Catalog, you can start by clicking on "Add Dataset" from the Home section. +Or click to Data Catalog (on the left side menu) and click “Add Dataset”.

+

Create dataset with upload csv

+

After that the below modal will be shown. You will need to select a connector. To upload a CSV file, we need to select “Upload CSV”.

+

Data Catalog connectors

+

Once you've selected the “Upload CSV” connector, a new screen will appear, enabling you to upload your file and designate a name for your connector. +This file upload connector will subsequently empower you to create one or more datasets from the same file at a later stage.

+ + + + + + + + + + + + + +
Loading areaUpload csv file
Upload file areaUpload CSV file
+

With the Connector created, you'll be able to add a dataset and specify its properties:

+
    +
  • Name: The name of your dataset;
  • +
  • Separator: This is an important parameter to make sure that we can parse your CSV correctly. The default value is “,”.
  • +
  • Data Type: Whether your dataset contains tabular or time-series (i.e., containing temporal dependency) data.
  • +
+

Add dataset details

+

Your created Connector (“Census File”) and Dataset (“Census”) will be added to the Data Catalog. +As soon as the status is green, you can navigate your Dataset. Click in Open Dataset as per the image below.

+

Explore dataset

+

Within the Dataset details, you can gain valuable insights through our automated data quality profiling. +This includes comprehensive metadata and an overview of your data, encompassing details like row count, identification +of duplicates, and insights into the overall quality of your dataset.

+

Dataset overview

+

Or perhaps, you want to further explore through visualization, the profile of your data with both univariate +and multivariate of your data.

+

Dataset profiling

+

Congrats! 🚀 You have now successfully created your first Connector and Dataset in Fabric’s Data Catalog. +Get ready for your journey of improved quality data for AI.

+ + + + + + +
+
+ + + + +
+ + + +
+ + + +
+
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/0.7/index.html b/0.7/index.html new file mode 100644 index 00000000..9b8bafb6 --- /dev/null +++ b/0.7/index.html @@ -0,0 +1,1396 @@ + + + + + + + + + + + + + + + + + + + + YData Fabric + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ + + + + + +
+ + +
+ +
+ + + + + + + + + +
+
+ + + +
+
+
+ + + + + + + + +
+
+
+ + + + +
+
+ + + + +

Welcome

+

YData Fabric is a Data-Centric AI development platform that accelerates AI development by helping data practitioners achieve production-quality data.

+

Much like for software engineering the quality of code is a must for the success of software development, Fabric +accounts for the data quality requirements for data-driven applications. It introduces standards, processes, and +acceleration to empower data science, analytics, and data engineering teams.

+

Data-Centric AI Approach

+ +

Try Fabric

+ +

Why adopt YData Fabric?

+

With Fabric, you can standardize the understanding of your data, quickly identify data quality issues, streamline and +version your data preparation workflows and finally leverage synthetic data for privacy-compliance or as a tool to boost ML +performance. Fabric is a development environment that supports a faster and easier process of preparing data for AI development. +Data practitioners are using Fabric to:

+
    +
  • Establish a centralized and collaborative repository for data projects.
  • +
  • Create and share comprehensive documentation of data, encompassing data schema, structure, and personally identifiable information (PII).
  • +
  • Prevent data quality issues with standardized data quality profiling, providing visual understanding and warnings on potential issues.
  • +
  • Accelerate data preparation with customizable recipes.
  • +
  • Improve machine learning performance with optimal data preparation through solutions such as synthetic data.
  • +
  • Shorten access to data with privacy-compliant synthetic data generatio.
  • +
  • Build and streamline data preparation workflows effortlessly through a user-friendly drag-and-drop interface.
  • +
  • Efficiently manage business rules, conduct comparisons, and implement version control for data workflows using pipelines.
  • +
+

📝 Key features

+

Data Catalog

+

Fabric Data Catalog provides a centralized perspective on datasets within a project-basis, optimizing data management +through seamless integration with the organization's existing data architectures via scalable connectors (e.g., MySQL, Google Cloud Storage, AWS S3). +It standardizes data quality profiling, streamlining the processes of efficient data cleaning and preparation, +while also automating the identification of Personally Identifiable Information (PII) to facilitate compliance with privacy regulations.

+

Explore how a Data Catalog through a centralized repository of your datasets, schema validation, and automated data profiling.

+

Labs

+

Fabric's Labs environments provide collaborative, scalable, and secure workspaces layered on a flexible infrastructure, enabling users to +seamlessly switch between CPUs and GPUs based on their computational needs. Labs are familiar environments that empower data developers with +powerful IDEs (Jupyter Notebooks, Visual Code or H2O flow) and a seamless experience with the tools they already love combined with YData's +cutting-edge SDK for data preparation.

+

Learn how to use the Labs to generate synthetic data in a familiar Python interface.

+

Synthetic data

+

Synthetic data, enabled by YData Fabric, provides data developers with a user-friendly interfaces (UI and code) for +generating artificial datasets, offering a versatile solution across formats like tabular, time-series and multi-table datasets. +The generated synthetic data holds the same value of the original and aligns intricately with specific business rules, contributing +to machine learning models enhancement, mitigation of privacy concerns and more robustness for data developments. +Fabric offers synthetic data that is ease to adapt and configure, allows customization in what concerns privacy-utility trade-offs.

+

Learn how you to create high-quality synthetic data within a user-friendly UI using Fabric’s data synthesis flow.

+

Pipelines

+

Fabric Pipelines streamlines data preparation workflows by automating, orchestrating, and optimizing data pipelines, +providing benefits such as flexibility, scalability, monitoring, and reproducibility for efficient and reliable data processing. +The intuitive drag-and-drop interface, leveraging Jupyter notebooks or Python scripts, expedites the pipeline setup process, +providing data developers with a quick and user-friendly experience.

+

Explore how you can leverage Fabric Pipelines to build versionable and reproducible data preparation workflows for ML development.

+

Tutorials

+

To understand how to best apply Fabric to your use cases, start by exploring the following tutorials:

+ +

You can find additional examples and use cases at YData Academy GitHub Repository.

+

🙋 Support

+

Facing an issue? We’re committed to providing all the support you need to ensure a smooth experience using Fabric:

+ + + + + + + +
+
+ + + + +
+ + + +
+ + + +
+
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/0.7/objects.inv b/0.7/objects.inv new file mode 100644 index 0000000000000000000000000000000000000000..ba0f5672d65a3669e9ca46b14b1c36fb2da0b7af GIT binary patch literal 1105 zcmV-X1g`rdAX9K?X>NERX>N99Zgg*Qc_4OWa&u{KZXhxWBOp+6Z)#;@bUGkeL}7Gc zAVy(ga%p18b#rNMXCQiPX<{x4c-qaHU2mH(7=`cmD=ICwBU*R2JCcwT zm8K~qt+E?LiPL!_5MeVZ`}N}^fC*m)zm|5>x(Pn#;f;N4;$998b(G`>aWV2;q`1N9 z6!^*}#D5(3VxT|ByE|XLyPiYH-TfUo{GNvBh{ELJsdL6ta`EX;MDSk}lIgoc(Dd^q zRgEuI*f*=1Up`&hywBNn{~d!eQw?=J*B181)L+&4-d^ep&Cs{|gwCNf1cd7_8;bp) zuO6hLRt-ksU2jqvU9O+QC0(vit`{>>WuE@(9%osPY1V^vuS!)F+bvt1zxF#%cvThK zevhzsu%j@YL&wN>DGq#SlhDBg*~cKX33X^hWO@-q?GG<_cO>4?2clBN(=uV#K4K5a zGdc$Z^fnT3w-~L3s=!osyif@cKV?re@EK=b3B| z>&1{~G1FHsJI&n7b6DzzCQp4ex8#XG?WNYS zS5*awZl^0GSFSCDSD)>$h^4+bCOc>)P$u#skhRDGWh~T15G$DlikB*jps|4mN|p$V zfK|g16fKh$fvZO?C}1hy0@<`dg53Kw=qqlB9Ln!Wm#S5KvjAE6OjS`|Rozf$J-gu1@(az-w%S6D_%cS9rM)q=jcV%al^prq zT5{oc>&H0u*K)*PRYPB>Tlnha%VqLfx=^--k9Y{)D32tn1r{_|VK5r79(M4IrAHrT z9(ZM=9-8;uq1h9|eK6T~wzYs2ug_I+kbgrILxC{T!;o&{8w@BH9)@!h_+T_w-C;2G z6#|T>lpcoEw5otXvEoCqs%sG-QYJhMr(yL1BVx4Mpqh8zz`MIf6W)g|C_A1fFDNgr z$1gs!_Ji}z52V`70qIAa)0yM=d$48K=-@AAHUP)j3@@dS8&~MV*^T+!#0-lE6TYMrGK=s5(5W{O Xz}M0~$KhiW%6QDUx%K`BR{ink(7+jz literal 0 HcmV?d00001 diff --git a/0.7/sdk/index.html b/0.7/sdk/index.html new file mode 100644 index 00000000..f4370fd2 --- /dev/null +++ b/0.7/sdk/index.html @@ -0,0 +1,1319 @@ + + + + + + + + + + + + + + + + + + + + + + Overview - YData Fabric + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ + + + + + +
+ + +
+ +
+ + + + + + + + + +
+
+ + + +
+
+
+ + + + + + + + +
+
+
+ + + + +
+
+ + + + +

Overview

+ +

+

YData Logo

+

+ +

pypi +Pythonversion +downloads

+
+

YData SDK for improved data quality everywhere!

+

ydata-sdk is here! Create a YData account so you can start using today!

+

Create account

+
+

Overview

+

The YData SDK is an ecosystem of methods that allows users to, through a python interface, adopt a Data-Centric approach towards the AI development. The solution includes a set of integrated components for data ingestion, standardized data quality evaluation and data improvement, such as synthetic data generation, allowing an iterative improvement of the datasets used in high-impact business applications.

+

Synthetic data can be used as Machine Learning performance enhancer, to augment or mitigate the presence of bias in real data. Furthermore, it can be used as a Privacy Enhancing Technology, to enable data-sharing initiatives or even to fuel testing environments.

+

Under the YData-SDK hood, you can find a set of algorithms and metrics based on statistics and deep learning based techniques, that will help you to accelerate your data preparation.

+

Current functionality

+

YData SDK is currently composed by the following main modules:

+
    +
  • +

    Datasources

    +
      +
    • YData’s SDK includes several connectors for easy integration with existing data sources. It supports several storage types, like filesystems and RDBMS. Check the list of connectors.
    • +
    • SDK’s Datasources run on top of Dask, which allows it to deal with not only small workloads but also larger volumes of data.
    • +
    +
  • +
  • +

    Synthesizers

    +
      +
    • Simplified interface to train a generative model and learn in a data-driven manner the behavior, the patterns and original data distribution. Optimize your model for privacy or utility use-cases.
    • +
    • From a trained synthesizer, you can generate synthetic samples as needed and parametrise the number of records needed.
    • +
    • Anonymization and privacy preserving capabilities to ensure that synthetic datasets does not contain Personal Identifiable Information (PII) and can safely be shared!
    • +
    • Conditional sampling can be used to restrict the domain and values of specific features in the sampled data.
    • +
    +
  • +
  • +

    Synthetic data quality report + Coming soon

    +
      +
    • An extensive synthetic data quality report that measures 3 dimensions: privacy, utility and fidelity of the generated data. The report can be downloaded in PDF format for ease of sharing and compliance purposes or as a JSON to enable the integration in data flows.
    • +
    +
  • +
  • +

    Profiling + Coming soon

    +
      +
    • A set of metrics and algorithms summarizes datasets quality in three main dimensions: warnings, univariate analysis and a multivariate perspective.
    • +
    +
  • +
+

Supported data formats

+
+
+
+

Tabular data synthesizer +The RegularSynthesizer is perfect to synthesize high-dimensional data, that is time-indepentent with high quality results.

+

Know more

+
+
+

Timeseries Synthesizer +The TimeSeriesSynthesizer is perfect to synthesize both regularly and not evenly spaced time-series, from smart-sensors to stock.

+

Know more

+
+
+

Transactional data synthesizer +The TimeSeriesSynthesizer supports transactional data, known to have highly irregular time intervals between records and directional relations between entities.

+

Coming soon

+

Know more

+
+
+

Relational databases synthesizer +The MultiTableSynthesizer is perfect to learn how to replicate the data within a relational database schema.

+

Coming soon

+

Know more

+
+
+
+ + + + + + +
+
+ + + + +
+ + + +
+ + + +
+
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/0.7/sdk/installation/index.html b/0.7/sdk/installation/index.html new file mode 100644 index 00000000..7bb173d8 --- /dev/null +++ b/0.7/sdk/installation/index.html @@ -0,0 +1,1321 @@ + + + + + + + + + + + + + + + + + + + + + + Installation - YData Fabric + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ + + + + + +
+ + +
+ +
+ + + + + + + + + +
+
+ + + +
+
+
+ + + + + + + + +
+
+
+ + + + +
+
+ + + + +

Installation

+ +

YData SDK is generally available through both Pypi and Conda allowing an easy process of installation. This experience allows combining YData SDK with other packages such as Pandas, Numpy or Scikit-Learn.

+

YData SDK is available for the public through a token-based authentication system. If you don’t have one yet, you can get your free license key during the installation process. You can check what features are available in the free version here.

+

Installing the package

+

YData SDK supports python versions bigger than python 3.8, and can be installed in Windows, Linux or MacOS operating systems.

+

Prior to the package installation, it is recommended the creation of a virtual or conda environment:

+
+
+
+
pyenv virtualenv 3.10 ydatasdk
+
+
+
+
+

And install ydata-sdk

+
+
+
+
pip install ydata-sdk
+
+
+
+
+

Authentication

+

Once you've installed ydata-sdk package you will need a token to run the functionalities. +YData SDK uses a token based authentication system. To get access to your token, you need to create a YData account.

+

YData SDK offers a free-trial and an enterprise version. To access your free-trial token, you need to create a YData account.

+

The token will be available here, after login:

+

SDK Token

+

With your account toke copied, you can set a new environment variable YDATA_TOKEN in the beginning of your development session.

+
    import os
+
+    os.setenv['YDATA_TOKEN'] = '{add-your-token}'
+
+

Once you have set your token, you are good to go to start exploring the incredible world of data-centric AI and smart synthetic data generation!

+

Check out our quickstart guide!

+ + + + + + +
+
+ + + + +
+ + + +
+ + + +
+
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/0.7/sdk/modules/connectors/index.html b/0.7/sdk/modules/connectors/index.html new file mode 100644 index 00000000..15b9451b --- /dev/null +++ b/0.7/sdk/modules/connectors/index.html @@ -0,0 +1,1339 @@ + + + + + + + + + + + + + + + + + + + + + + Connectors - YData Fabric + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ + + + + + +
+ + +
+ +
+ + + + + + + + + +
+
+ + + +
+
+
+ + + + + + + + +
+
+
+ + + + +
+
+ + + + +

Connectors

+

YData SDK allows users to consume data assets from remote storages through Connectors. YData Connectors support different types of storages, from filesystems to RDBMS'.

+

Below the list of available connectors:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Connector NameTypeSupported File TypesUseful LinksNotes
AWS S3Remote object storageCSV, Parquethttps://aws.amazon.com/s3/
Google Cloud StorageRemote object storageCSV, Parquethttps://cloud.google.com/storage
Azure Blob StorageRemote object storageCSV, Parquethttps://azure.microsoft.com/en-us/services/storage/blobs/
File UploadLocalCSV-Maximum file size is 220MB. Bigger files should be uploaded and read from remote object storages
MySQLRDBMSNot applicablehttps://www.mysql.com/Supports reading whole schemas or specifying a query
Azure SQL ServerRDBMSNot applicablehttps://azure.microsoft.com/en-us/services/sql-database/campaign/Supports reading whole schemas or specifying a query
PostgreSQLRDBMSNot applicablehttps://www.postgresql.org/Supports reading whole schemas or specifying a query
SnowflakeRDBMSNot applicablehttps://docs.snowflake.com/en/sql-reference-commandsSupports reading whole schemas or specifying a query
Google BigQueryData warehouseNot applicablehttps://cloud.google.com/bigquery
Azure Data LakeData lakeCSV, Parquethttps://azure.microsoft.com/en-us/services/storage/data-lake-storage/
+

More details can be found at Connectors APi Reference Docs.

+ + + + + + +
+
+ + + + +
+ + + +
+ + + +
+
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/0.7/sdk/modules/synthetic_data/index.html b/0.7/sdk/modules/synthetic_data/index.html new file mode 100644 index 00000000..c765dba1 --- /dev/null +++ b/0.7/sdk/modules/synthetic_data/index.html @@ -0,0 +1,1202 @@ + + + + + + + + + + + + + + + + + + Synthetic data generation - YData Fabric + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ + + + + + +
+ + +
+ +
+ + + + + + + + + +
+
+ + + +
+
+
+ + + + + + + + +
+
+
+ + + + +
+
+ + + + +

Synthetic data generation

+

Data formats

+

Tabular data

+

Time-series data

+

Transactions data

+

Best practices

+ + + + + + +
+
+ + + + +
+ + + +
+ +
+ + + + +
+ +
+
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/0.7/sdk/quickstart/index.html b/0.7/sdk/quickstart/index.html new file mode 100644 index 00000000..115d0dee --- /dev/null +++ b/0.7/sdk/quickstart/index.html @@ -0,0 +1,1352 @@ + + + + + + + + + + + + + + + + + + + + + + Quickstart - YData Fabric + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ + + + + + +
+ + +
+ +
+ + + + + + + + + +
+
+ + + +
+
+
+ + + + + + + + +
+
+
+ + + + +
+
+ + + + +

Quickstart

+

YData SDK allows you to with an easy and familiar interface, to adopt a Data-Centric AI approach for the development of Machine Learning solutions. +YData SDK features were designed to support structure data, including tabular data, time-series and transactional data.

+

Read data

+

To start leveraging the package features you should consume your data either through the Connectors or pandas.Dataframe. +The list of available connectors can be found here [add a link].

+
+
+
+
    # Example for a Google Cloud Storage Connector
+    credentials = "{insert-credentials-file-path}"
+
+    # We create a new connector for Google Cloud Storage
+    connector = Connector(connector_type='gcs', credentials=credentials)
+
+    # Create a Datasource from the connector
+    # Note that a connector can be re-used for several datasources
+    X = DataSource(connector=connector, path='gs://<my_bucket>.csv')
+
+
+
+
    # Load a small dataset
+    X = pd.read_csv('{insert-file-path.csv}')
+
+    # Init a synthesizer
+    synth = RegularSynthesizer()
+
+    # Train the synthesizer with the pandas Dataframe as input
+    # The data is then sent to the cluster for processing
+    synth.fit(X)
+
+
+
+
+

The synthesis process returns a pandas.DataFrame object. +Note that if you are using the ydata-sdk free version, all of your data is sent to a remote cluster on YData's infrastructure.

+

Data synthesis flow

+

The process of data synthesis can be described into the following steps:

+
stateDiagram-v2
+  state read_data
+  read_data --> init_synth
+  init_synth --> train_synth
+  train_synth --> generate_samples
+  generate_samples --> [*]
+

The code snippet below shows how easy can be to start generating new synthetic data. The package includes a set of examples datasets for a quickstart.

+
    from ydata.sdk.dataset import get_dataset
+
+    #read the example data
+    X = get_dataset('census')
+
+    # Init a synthesizer
+    synth = RegularSynthesizer()
+
+    # Fit the synthesizer to the input data
+    synth.fit(X)
+
+    # Sample new synthetic data. The below request ask for new 1000 synthetic rows
+    synth.sample(n_samples=1000)
+
+
+

Do I need to prepare my data before synthesis?

+

The sdk ensures that the original behaviour is replicated. For that reason, there is no need to preprocess outlier observations or missing data.

+

By default all the missing data is replicated as NaN.

+
+ + + + + + +
+
+ + + + +
+ + + +
+ + + +
+
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/0.7/sdk/reference/api/common/client/index.html b/0.7/sdk/reference/api/common/client/index.html new file mode 100644 index 00000000..82577961 --- /dev/null +++ b/0.7/sdk/reference/api/common/client/index.html @@ -0,0 +1,2808 @@ + + + + + + + + + + + + + + + + + + + + + + Client - YData Fabric + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ + + + + + +
+ + +
+ +
+ + + + + + + + + +
+
+ + + +
+
+
+ + + + + + + + +
+
+
+ + + + +
+
+ + + + +

Get client

+ + + +
+ + + + +
+ +

Deduce how to initialize or retrieve the client.

+

This is meant to be a zero configuration for the user.

+ +
+ Create and set a client globally +
from ydata.sdk.client import get_client
+get_client(set_as_global=True)
+
+
+ + +

Parameters:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescriptionDefault
client_or_creds + Optional[Union[Client, dict, str, Path]] + +
+

Client to forward or credentials for initialization

+
+
+ None +
set_as_global + bool + +
+

If True, set client as global

+
+
+ False +
wait_for_auth + bool + +
+

If True, wait for the user to authenticate

+
+
+ True +
+ + + +

Returns:

+ + + + + + + + + + + + + +
TypeDescription
+ Client + +
+

Client instance

+
+
+ +
+ Source code in ydata/sdk/common/client/utils.py +
18
+19
+20
+21
+22
+23
+24
+25
+26
+27
+28
+29
+30
+31
+32
+33
+34
+35
+36
+37
+38
+39
+40
+41
+42
+43
+44
+45
+46
+47
+48
+49
+50
+51
+52
+53
+54
+55
+56
+57
+58
+59
+60
+61
+62
+63
+64
+65
+66
+67
+68
+69
+70
+71
+72
+73
+74
+75
+76
+77
+78
+79
+80
+81
+82
+83
+84
+85
+86
+87
+88
+89
+90
+91
def get_client(client_or_creds: Optional[Union[Client, Dict, str, Path]] = None, set_as_global: bool = False, wait_for_auth: bool = True) -> Client:
+    """Deduce how to initialize or retrieve the client.
+
+    This is meant to be a zero configuration for the user.
+
+    Example: Create and set a client globally
+            ```py
+            from ydata.sdk.client import get_client
+            get_client(set_as_global=True)
+            ```
+
+    Args:
+        client_or_creds (Optional[Union[Client, dict, str, Path]]): Client to forward or credentials for initialization
+        set_as_global (bool): If `True`, set client as global
+        wait_for_auth (bool): If `True`, wait for the user to authenticate
+
+    Returns:
+        Client instance
+    """
+    client = None
+    global WAITING_FOR_CLIENT
+    try:
+
+        # If a client instance is set globally, return it
+        if not set_as_global and Client.GLOBAL_CLIENT is not None:
+            return Client.GLOBAL_CLIENT
+
+        # Client exists, forward it
+        if isinstance(client_or_creds, Client):
+            return client_or_creds
+
+        # Explicit credentials
+        ''' # For the first version, we deactivate explicit credentials via string or file for env var only
+        if isinstance(client_or_creds, (dict, str, Path)):
+            if isinstance(client_or_creds, str):  # noqa: SIM102
+                if Path(client_or_creds).is_file():
+                    client_or_creds = Path(client_or_creds)
+
+            if isinstance(client_or_creds, Path):
+                client_or_creds = json.loads(client_or_creds.open().read())
+
+            return Client(credentials=client_or_creds)
+
+        # Last try with environment variables
+        #if client_or_creds is None:
+        client = _client_from_env(wait_for_auth=wait_for_auth)
+        '''
+        credentials = environ.get(TOKEN_VAR)
+        if credentials is not None:
+            client = Client(credentials=credentials)
+
+    except ClientHandshakeError as e:
+        wait_for_auth = False  # For now deactivate wait_for_auth until the backend is ready
+        if wait_for_auth:
+            WAITING_FOR_CLIENT = True
+            start = time()
+            login_message_printed = False
+            while client is None:
+                if not login_message_printed:
+                    print(
+                        f"The token needs to be refreshed - please validate your token by browsing at the following URL:\n\n\t{e.auth_link}")
+                    login_message_printed = True
+                with suppress(ClientCreationError):
+                    sleep(BACKOFF)
+                    client = get_client(wait_for_auth=False)
+                now = time()
+                if now - start > CLIENT_INIT_TIMEOUT:
+                    WAITING_FOR_CLIENT = False
+                    break
+
+    if client is None and not WAITING_FOR_CLIENT:
+        sys.tracebacklimit = None
+        raise ClientCreationError
+    return client
+
+
+
+ +
+ +
+ + + + +
+ + +

Main Client class used to abstract the connection to the backend.

+

A normal user should not have to instanciate a Client by itself. +However, in the future it will be useful for power-users to manage projects and connections.

+ + + +

Parameters:

+ + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescriptionDefault
credentials + Optional[dict] + +
+

(optional) Credentials to connect

+
+
+ None +
project + Optional[Project] + +
+

(optional) Project to connect to. If not specified, the client will connect to the default user's project.

+
+
+ None +
+ +
+ Source code in ydata/sdk/common/client/client.py +
@typechecked
+class Client(metaclass=SingletonClient):
+    """Main Client class used to abstract the connection to the backend.
+
+    A normal user should not have to instanciate a [`Client`][ydata.sdk.common.client.Client] by itself.
+    However, in the future it will be useful for power-users to manage projects and connections.
+
+    Args:
+        credentials (Optional[dict]): (optional) Credentials to connect
+        project (Optional[Project]): (optional) Project to connect to. If not specified, the client will connect to the default user's project.
+    """
+
+    codes = codes
+
+    def __init__(self, credentials: Optional[Union[str, Dict]] = None, project: Optional[Project] = None, set_as_global: bool = False):
+        self._base_url = environ.get("YDATA_BASE_URL", DEFAULT_URL)
+        self._scheme = 'https'
+        self._headers = {'Authorization': credentials}
+        self._http_client = httpClient(
+            headers=self._headers, timeout=Timeout(10, read=None))
+
+        self._handshake()
+
+        self._default_project = project or self._get_default_project(credentials)
+        if set_as_global:
+            self.__set_global()
+
+    def post(self, endpoint: str, data: Optional[Dict] = None, json: Optional[Dict] = None,
+             project: Project | None = None, files: Optional[Dict] = None, raise_for_status: bool = True) -> Response:
+        """POST request to the backend.
+
+        Args:
+            endpoint (str): POST endpoint
+            data (Optional[dict]): (optional) multipart form data
+            json (Optional[dict]): (optional) json data
+            files (Optional[dict]): (optional) files to be sent
+            raise_for_status (bool): raise an exception on error
+
+        Returns:
+            Response object
+        """
+        url_data = self.__build_url(
+            endpoint, data=data, json=json, files=files, project=project)
+        response = self._http_client.post(**url_data)
+
+        if response.status_code != Client.codes.OK and raise_for_status:
+            self.__raise_for_status(response)
+
+        return response
+
+    def get(self, endpoint: str, params: Optional[Dict] = None,
+            project: Project | None = None, cookies: Optional[Dict] = None, raise_for_status: bool = True) -> Response:
+        """GET request to the backend.
+
+        Args:
+            endpoint (str): GET endpoint
+            cookies (Optional[dict]): (optional) cookies data
+            raise_for_status (bool): raise an exception on error
+
+        Returns:
+            Response object
+        """
+        url_data = self.__build_url(endpoint, params=params,
+                                    cookies=cookies, project=project)
+        response = self._http_client.get(**url_data)
+
+        if response.status_code != Client.codes.OK and raise_for_status:
+            self.__raise_for_status(response)
+
+        return response
+
+    def get_static_file(self, endpoint: str, project: Project | None = None, raise_for_status: bool = True) -> Response:
+        """Retrieve a static file from the backend.
+
+        Args:
+            endpoint (str): GET endpoint
+            raise_for_status (bool): raise an exception on error
+
+        Returns:
+            Response object
+        """
+        url_data = self.__build_url(endpoint, project=project)
+        url_data['url'] = f'{self._scheme}://{self._base_url}/static-content{endpoint}'
+        response = self._http_client.get(**url_data)
+
+        if response.status_code != Client.codes.OK and raise_for_status:
+            self.__raise_for_status(response)
+
+        return response
+
+    def _handshake(self):
+        """Client handshake.
+
+        It is used to determine is the client can connect with its
+        current authorization token.
+        """
+        response = self.get('/profiles', params={}, raise_for_status=False)
+        if response.status_code == Client.codes.FOUND:
+            parser = LinkExtractor()
+            parser.feed(response.text)
+            raise ClientHandshakeError(auth_link=parser.link)
+
+    def _get_default_project(self, token: str):
+        response = self.get('/profiles/me', params={}, cookies={'access_token': token})
+        data: Dict = response.json()
+        return data['myWorkspace']
+
+    def __build_url(self, endpoint: str, params: Optional[Dict] = None, data: Optional[Dict] = None,
+                    json: Optional[Dict] = None, project: Project | None = None, files: Optional[Dict] = None,
+                    cookies: Optional[Dict] = None) -> Dict:
+        """Build a request for the backend.
+
+        Args:
+            endpoint (str): backend endpoint
+            params (Optional[dict]): URL parameters
+            data (Optional[Project]): (optional) multipart form data
+            json (Optional[dict]): (optional) json data
+            files (Optional[dict]): (optional) files to be sent
+            cookies (Optional[dict]): (optional) cookies data
+
+        Returns:
+            dictionary containing the information to perform a request
+        """
+        _params = params if params is not None else {
+            'ns': project or self._default_project
+        }
+
+        url_data = {
+            'url': f'{self._scheme}://{self._base_url}/api{endpoint}',
+            'headers': self._headers,
+            'params': _params,
+        }
+
+        if data is not None:
+            url_data['data'] = data
+
+        if json is not None:
+            url_data['json'] = json
+
+        if files is not None:
+            url_data['files'] = files
+
+        if cookies is not None:
+            url_data['cookies'] = cookies
+
+        return url_data
+
+    def __set_global(self) -> None:
+        """Sets a client instance as global."""
+        # If the client is stateful, close it gracefully!
+        Client.GLOBAL_CLIENT = self
+
+    def __raise_for_status(self, response: Response) -> None:
+        """Raise an exception if the response is not OK.
+
+        When an exception is raised, we try to convert it to a ResponseError which is
+        a wrapper around a backend error. This usually gives enough context and provides
+        nice error message.
+
+        If it cannot be converted to ResponseError, it is re-raised.
+
+        Args:
+            response (Response): response to analyze
+        """
+        try:
+            response.raise_for_status()
+        except HTTPStatusError as e:
+            with suppress(Exception):
+                e = ResponseError(**response.json())
+            raise e
+
+
+ + + +
+ + + + + + + + + + +
+ + + + +

+ __build_url(endpoint, params=None, data=None, json=None, project=None, files=None, cookies=None) + +

+ + +
+ +

Build a request for the backend.

+ + + +

Parameters:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescriptionDefault
endpoint + str + +
+

backend endpoint

+
+
+ required +
params + Optional[dict] + +
+

URL parameters

+
+
+ None +
data + Optional[Project] + +
+

(optional) multipart form data

+
+
+ None +
json + Optional[dict] + +
+

(optional) json data

+
+
+ None +
files + Optional[dict] + +
+

(optional) files to be sent

+
+
+ None +
cookies + Optional[dict] + +
+

(optional) cookies data

+
+
+ None +
+ + + +

Returns:

+ + + + + + + + + + + + + +
TypeDescription
+ Dict + +
+

dictionary containing the information to perform a request

+
+
+ +
+ Source code in ydata/sdk/common/client/client.py +
def __build_url(self, endpoint: str, params: Optional[Dict] = None, data: Optional[Dict] = None,
+                json: Optional[Dict] = None, project: Project | None = None, files: Optional[Dict] = None,
+                cookies: Optional[Dict] = None) -> Dict:
+    """Build a request for the backend.
+
+    Args:
+        endpoint (str): backend endpoint
+        params (Optional[dict]): URL parameters
+        data (Optional[Project]): (optional) multipart form data
+        json (Optional[dict]): (optional) json data
+        files (Optional[dict]): (optional) files to be sent
+        cookies (Optional[dict]): (optional) cookies data
+
+    Returns:
+        dictionary containing the information to perform a request
+    """
+    _params = params if params is not None else {
+        'ns': project or self._default_project
+    }
+
+    url_data = {
+        'url': f'{self._scheme}://{self._base_url}/api{endpoint}',
+        'headers': self._headers,
+        'params': _params,
+    }
+
+    if data is not None:
+        url_data['data'] = data
+
+    if json is not None:
+        url_data['json'] = json
+
+    if files is not None:
+        url_data['files'] = files
+
+    if cookies is not None:
+        url_data['cookies'] = cookies
+
+    return url_data
+
+
+
+ +
+ + +
+ + + + +

+ __raise_for_status(response) + +

+ + +
+ +

Raise an exception if the response is not OK.

+

When an exception is raised, we try to convert it to a ResponseError which is +a wrapper around a backend error. This usually gives enough context and provides +nice error message.

+

If it cannot be converted to ResponseError, it is re-raised.

+ + + +

Parameters:

+ + + + + + + + + + + + + + + + + +
NameTypeDescriptionDefault
response + Response + +
+

response to analyze

+
+
+ required +
+ +
+ Source code in ydata/sdk/common/client/client.py +
def __raise_for_status(self, response: Response) -> None:
+    """Raise an exception if the response is not OK.
+
+    When an exception is raised, we try to convert it to a ResponseError which is
+    a wrapper around a backend error. This usually gives enough context and provides
+    nice error message.
+
+    If it cannot be converted to ResponseError, it is re-raised.
+
+    Args:
+        response (Response): response to analyze
+    """
+    try:
+        response.raise_for_status()
+    except HTTPStatusError as e:
+        with suppress(Exception):
+            e = ResponseError(**response.json())
+        raise e
+
+
+
+ +
+ + +
+ + + + +

+ __set_global() + +

+ + +
+ +

Sets a client instance as global.

+ +
+ Source code in ydata/sdk/common/client/client.py +
def __set_global(self) -> None:
+    """Sets a client instance as global."""
+    # If the client is stateful, close it gracefully!
+    Client.GLOBAL_CLIENT = self
+
+
+
+ +
+ + +
+ + + + +

+ get(endpoint, params=None, project=None, cookies=None, raise_for_status=True) + +

+ + +
+ +

GET request to the backend.

+ + + +

Parameters:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescriptionDefault
endpoint + str + +
+

GET endpoint

+
+
+ required +
cookies + Optional[dict] + +
+

(optional) cookies data

+
+
+ None +
raise_for_status + bool + +
+

raise an exception on error

+
+
+ True +
+ + + +

Returns:

+ + + + + + + + + + + + + +
TypeDescription
+ Response + +
+

Response object

+
+
+ +
+ Source code in ydata/sdk/common/client/client.py +
def get(self, endpoint: str, params: Optional[Dict] = None,
+        project: Project | None = None, cookies: Optional[Dict] = None, raise_for_status: bool = True) -> Response:
+    """GET request to the backend.
+
+    Args:
+        endpoint (str): GET endpoint
+        cookies (Optional[dict]): (optional) cookies data
+        raise_for_status (bool): raise an exception on error
+
+    Returns:
+        Response object
+    """
+    url_data = self.__build_url(endpoint, params=params,
+                                cookies=cookies, project=project)
+    response = self._http_client.get(**url_data)
+
+    if response.status_code != Client.codes.OK and raise_for_status:
+        self.__raise_for_status(response)
+
+    return response
+
+
+
+ +
+ + +
+ + + + +

+ get_static_file(endpoint, project=None, raise_for_status=True) + +

+ + +
+ +

Retrieve a static file from the backend.

+ + + +

Parameters:

+ + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescriptionDefault
endpoint + str + +
+

GET endpoint

+
+
+ required +
raise_for_status + bool + +
+

raise an exception on error

+
+
+ True +
+ + + +

Returns:

+ + + + + + + + + + + + + +
TypeDescription
+ Response + +
+

Response object

+
+
+ +
+ Source code in ydata/sdk/common/client/client.py +
def get_static_file(self, endpoint: str, project: Project | None = None, raise_for_status: bool = True) -> Response:
+    """Retrieve a static file from the backend.
+
+    Args:
+        endpoint (str): GET endpoint
+        raise_for_status (bool): raise an exception on error
+
+    Returns:
+        Response object
+    """
+    url_data = self.__build_url(endpoint, project=project)
+    url_data['url'] = f'{self._scheme}://{self._base_url}/static-content{endpoint}'
+    response = self._http_client.get(**url_data)
+
+    if response.status_code != Client.codes.OK and raise_for_status:
+        self.__raise_for_status(response)
+
+    return response
+
+
+
+ +
+ + +
+ + + + +

+ post(endpoint, data=None, json=None, project=None, files=None, raise_for_status=True) + +

+ + +
+ +

POST request to the backend.

+ + + +

Parameters:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescriptionDefault
endpoint + str + +
+

POST endpoint

+
+
+ required +
data + Optional[dict] + +
+

(optional) multipart form data

+
+
+ None +
json + Optional[dict] + +
+

(optional) json data

+
+
+ None +
files + Optional[dict] + +
+

(optional) files to be sent

+
+
+ None +
raise_for_status + bool + +
+

raise an exception on error

+
+
+ True +
+ + + +

Returns:

+ + + + + + + + + + + + + +
TypeDescription
+ Response + +
+

Response object

+
+
+ +
+ Source code in ydata/sdk/common/client/client.py +
63
+64
+65
+66
+67
+68
+69
+70
+71
+72
+73
+74
+75
+76
+77
+78
+79
+80
+81
+82
+83
+84
def post(self, endpoint: str, data: Optional[Dict] = None, json: Optional[Dict] = None,
+         project: Project | None = None, files: Optional[Dict] = None, raise_for_status: bool = True) -> Response:
+    """POST request to the backend.
+
+    Args:
+        endpoint (str): POST endpoint
+        data (Optional[dict]): (optional) multipart form data
+        json (Optional[dict]): (optional) json data
+        files (Optional[dict]): (optional) files to be sent
+        raise_for_status (bool): raise an exception on error
+
+    Returns:
+        Response object
+    """
+    url_data = self.__build_url(
+        endpoint, data=data, json=json, files=files, project=project)
+    response = self._http_client.post(**url_data)
+
+    if response.status_code != Client.codes.OK and raise_for_status:
+        self.__raise_for_status(response)
+
+    return response
+
+
+
+ +
+ + + +
+ +
+ +
+ + + + + + +
+
+ + + + +
+ + + +
+ + + +
+
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/0.7/sdk/reference/api/common/types/index.html b/0.7/sdk/reference/api/common/types/index.html new file mode 100644 index 00000000..e130036b --- /dev/null +++ b/0.7/sdk/reference/api/common/types/index.html @@ -0,0 +1,1292 @@ + + + + + + + + + + + + + + + + + + + + Types - YData Fabric + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ + + + + + +
+ + +
+ +
+ + + + + + + + + +
+
+ + + +
+
+
+ + + + + + + + +
+
+
+ + + + +
+
+ + + + +

Types

+ +
+ + + + +
+
+ +
+ +
+ + + + +
+
+ +
+ + + + + + +
+
+ + + + +
+ + + +
+ + + +
+
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/0.7/sdk/reference/api/connectors/connector/index.html b/0.7/sdk/reference/api/connectors/connector/index.html new file mode 100644 index 00000000..4a7b1a1b --- /dev/null +++ b/0.7/sdk/reference/api/connectors/connector/index.html @@ -0,0 +1,2417 @@ + + + + + + + + + + + + + + + + + + + + + + Connector - YData Fabric + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ + + + + + +
+ + +
+ +
+ + + + + + + + + +
+
+ + + +
+
+
+ + + + + + + + +
+
+
+ + + + +
+
+ + + + +
+ + + + +
+

+ Bases: ModelFactoryMixin

+ + +

A Connector allows to connect and +access data stored in various places. The list of available connectors can +be found here.

+ + + +

Parameters:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescriptionDefault
connector_type + Union[ConnectorType, str] + +
+

Type of the connector to be created

+
+
+ None +
credentials + dict + +
+

Connector credentials

+
+
+ None +
name + Optional[str] + +
+

(optional) Connector name

+
+
+ None +
client + Client + +
+

(optional) Client to connect to the backend

+
+
+ None +
+ + + +

Attributes:

+ + + + + + + + + + + + + + + + + + + + +
NameTypeDescription
uid + UID + +
+

UID fo the connector instance (creating internally)

+
+
type + ConnectorType + +
+

Type of the connector

+
+
+ +
+ Source code in ydata/sdk/connectors/connector.py +
class Connector(ModelFactoryMixin):
+    """A [`Connector`][ydata.sdk.connectors.Connector] allows to connect and
+    access data stored in various places. The list of available connectors can
+    be found [here][ydata.sdk.connectors.ConnectorType].
+
+    Arguments:
+        connector_type (Union[ConnectorType, str]): Type of the connector to be created
+        credentials (dict): Connector credentials
+        name (Optional[str]): (optional) Connector name
+        client (Client): (optional) Client to connect to the backend
+
+    Attributes:
+        uid (UID): UID fo the connector instance (creating internally)
+        type (ConnectorType): Type of the connector
+    """
+
+    def __init__(self, connector_type: Union[ConnectorType, str] = None, credentials: Optional[Dict] = None,  name: Optional[str] = None, client: Optional[Client] = None):
+        self._init_common(client=client)
+        self._model: Optional[mConnector] = self._create_model(
+            connector_type, credentials, name, client=client)
+
+    @init_client
+    def _init_common(self, client: Optional[Client] = None):
+        self._client = client
+        self._logger = create_logger(__name__, level=LOG_LEVEL)
+
+    @property
+    def uid(self) -> UID:
+        return self._model.uid
+
+    @property
+    def type(self) -> str:
+        return self._model.type
+
+    @staticmethod
+    @init_client
+    def get(uid: UID, client: Optional[Client] = None) -> "Connector":
+        """Get an existing connector.
+
+        Arguments:
+            uid (UID): Connector identifier
+            client (Client): (optional) Client to connect to the backend
+
+        Returns:
+            Connector
+        """
+        connectors: ConnectorsList = Connector.list(client=client)
+        data = connectors.get_by_uid(uid)
+        model = mConnector(**data)
+        connector = ModelFactoryMixin._init_from_model_data(Connector, model)
+        return connector
+
+    @staticmethod
+    def _init_connector_type(connector_type: Union[ConnectorType, str]) -> ConnectorType:
+        if isinstance(connector_type, str):
+            try:
+                connector_type = ConnectorType(connector_type)
+            except Exception:
+                c_list = ", ".join([c.value for c in ConnectorType])
+                raise InvalidConnectorError(
+                    f"ConnectorType '{connector_type}' does not exist.\nValid connector types are: {c_list}.")
+        return connector_type
+
+    @staticmethod
+    def _init_credentials(connector_type: ConnectorType, credentials: Union[str, Path, Dict, Credentials]) -> Credentials:
+        _credentials = None
+
+        if isinstance(credentials, str):
+            credentials = Path(credentials)
+
+        if isinstance(credentials, Path):
+            try:
+                _credentials = json_loads(credentials.open().read())
+            except Exception:
+                raise CredentialTypeError(
+                    'Could not read the credentials. Please, check your path or credentials structure.')
+
+        try:
+            from ydata.sdk.connectors._models.connector_map import TYPE_TO_CLASS
+            credential_cls = TYPE_TO_CLASS.get(connector_type.value)
+            _credentials = credential_cls(**_credentials)
+        except Exception:
+            raise CredentialTypeError(
+                "Could not create the credentials. Verify the path or the structure your credentials.")
+
+        return _credentials
+
+    @staticmethod
+    def create(connector_type: Union[ConnectorType, str], credentials: Union[str, Path, Dict, Credentials], name: Optional[str] = None, client: Optional[Client] = None) -> "Connector":
+        """Create a new connector.
+
+        Arguments:
+            connector_type (Union[ConnectorType, str]): Type of the connector to be created
+            credentials (dict): Connector credentials
+            name (Optional[str]): (optional) Connector name
+            client (Client): (optional) Client to connect to the backend
+
+        Returns:
+            New connector
+        """
+        model = Connector._create_model(
+            connector_type=connector_type, credentials=credentials, name=name, client=client)
+        connector = ModelFactoryMixin._init_from_model_data(
+            Connector, model)
+        return connector
+
+    @classmethod
+    @init_client
+    def _create_model(cls, connector_type: Union[ConnectorType, str], credentials: Union[str, Path, Dict, Credentials], name: Optional[str] = None, client: Optional[Client] = None) -> mConnector:
+        _name = name if name is not None else str(uuid4())
+        _connector_type = Connector._init_connector_type(connector_type)
+        _credentials = Connector._init_credentials(_connector_type, credentials)
+        payload = {
+            "type": _connector_type.value,
+            "credentials": _credentials.dict(by_alias=True),
+            "name": _name
+        }
+        response = client.post('/connector/', json=payload)
+        data: list = response.json()
+
+        return mConnector(**data)
+
+    @staticmethod
+    @init_client
+    def list(client: Optional[Client] = None) -> ConnectorsList:
+        """List the connectors instances.
+
+        Arguments:
+            client (Client): (optional) Client to connect to the backend
+
+        Returns:
+            List of connectors
+        """
+        response = client.get('/connector')
+        data: list = response.json()
+        return ConnectorsList(data)
+
+    def __repr__(self):
+        return self._model.__repr__()
+
+
+ + + +
+ + + + + + + + + + +
+ + + + +

+ create(connector_type, credentials, name=None, client=None) + + + staticmethod + + +

+ + +
+ +

Create a new connector.

+ + + +

Parameters:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescriptionDefault
connector_type + Union[ConnectorType, str] + +
+

Type of the connector to be created

+
+
+ required +
credentials + dict + +
+

Connector credentials

+
+
+ required +
name + Optional[str] + +
+

(optional) Connector name

+
+
+ None +
client + Client + +
+

(optional) Client to connect to the backend

+
+
+ None +
+ + + +

Returns:

+ + + + + + + + + + + + + +
TypeDescription
+ Connector + +
+

New connector

+
+
+ +
+ Source code in ydata/sdk/connectors/connector.py +
@staticmethod
+def create(connector_type: Union[ConnectorType, str], credentials: Union[str, Path, Dict, Credentials], name: Optional[str] = None, client: Optional[Client] = None) -> "Connector":
+    """Create a new connector.
+
+    Arguments:
+        connector_type (Union[ConnectorType, str]): Type of the connector to be created
+        credentials (dict): Connector credentials
+        name (Optional[str]): (optional) Connector name
+        client (Client): (optional) Client to connect to the backend
+
+    Returns:
+        New connector
+    """
+    model = Connector._create_model(
+        connector_type=connector_type, credentials=credentials, name=name, client=client)
+    connector = ModelFactoryMixin._init_from_model_data(
+        Connector, model)
+    return connector
+
+
+
+ +
+ + +
+ + + + +

+ get(uid, client=None) + + + staticmethod + + +

+ + +
+ +

Get an existing connector.

+ + + +

Parameters:

+ + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescriptionDefault
uid + UID + +
+

Connector identifier

+
+
+ required +
client + Client + +
+

(optional) Client to connect to the backend

+
+
+ None +
+ + + +

Returns:

+ + + + + + + + + + + + + +
TypeDescription
+ Connector + +
+

Connector

+
+
+ +
+ Source code in ydata/sdk/connectors/connector.py +
53
+54
+55
+56
+57
+58
+59
+60
+61
+62
+63
+64
+65
+66
+67
+68
+69
@staticmethod
+@init_client
+def get(uid: UID, client: Optional[Client] = None) -> "Connector":
+    """Get an existing connector.
+
+    Arguments:
+        uid (UID): Connector identifier
+        client (Client): (optional) Client to connect to the backend
+
+    Returns:
+        Connector
+    """
+    connectors: ConnectorsList = Connector.list(client=client)
+    data = connectors.get_by_uid(uid)
+    model = mConnector(**data)
+    connector = ModelFactoryMixin._init_from_model_data(Connector, model)
+    return connector
+
+
+
+ +
+ + +
+ + + + +

+ list(client=None) + + + staticmethod + + +

+ + +
+ +

List the connectors instances.

+ + + +

Parameters:

+ + + + + + + + + + + + + + + + + +
NameTypeDescriptionDefault
client + Client + +
+

(optional) Client to connect to the backend

+
+
+ None +
+ + + +

Returns:

+ + + + + + + + + + + + + +
TypeDescription
+ ConnectorsList + +
+

List of connectors

+
+
+ +
+ Source code in ydata/sdk/connectors/connector.py +
@staticmethod
+@init_client
+def list(client: Optional[Client] = None) -> ConnectorsList:
+    """List the connectors instances.
+
+    Arguments:
+        client (Client): (optional) Client to connect to the backend
+
+    Returns:
+        List of connectors
+    """
+    response = client.get('/connector')
+    data: list = response.json()
+    return ConnectorsList(data)
+
+
+
+ +
+ + + +
+ +
+ +

ConnectorType

+ + +
+ + + + +
+

+ Bases: Enum

+ + + + + +
+ + + + + + + +
+ + + + +

+ AWS_S3 = 'aws-s3' + + + class-attribute + instance-attribute + + +

+ + +
+ +

AWS S3 connector

+
+ +
+ +
+ + + + +

+ AZURE_BLOB = 'azure-blob' + + + class-attribute + instance-attribute + + +

+ + +
+ +

Azure Blob connector

+
+ +
+ +
+ + + + +

+ AZURE_SQL = 'azure-sql' + + + class-attribute + instance-attribute + + +

+ + +
+ +

AzureSQL connector

+
+ +
+ +
+ + + + +

+ BIGQUERY = 'google-bigquery' + + + class-attribute + instance-attribute + + +

+ + +
+ +

BigQuery connector

+
+ +
+ +
+ + + + +

+ FILE = 'file' + + + class-attribute + instance-attribute + + +

+ + +
+ +

File connector (placeholder)

+
+ +
+ +
+ + + + +

+ GCS = 'gcs' + + + class-attribute + instance-attribute + + +

+ + +
+ +

Google Cloud Storage connector

+
+ +
+ +
+ + + + +

+ MYSQL = 'mysql' + + + class-attribute + instance-attribute + + +

+ + +
+ +

MySQL connector

+
+ +
+ +
+ + + + +

+ SNOWFLAKE = 'snowflake' + + + class-attribute + instance-attribute + + +

+ + +
+ +

Snowflake connector

+
+ +
+ + + + + +
+ +
+ +
+ + + + + + +
+
+ + + + +
+ + + +
+ + + +
+
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/0.7/sdk/reference/api/datasources/datasource/index.html b/0.7/sdk/reference/api/datasources/datasource/index.html new file mode 100644 index 00000000..448aad3d --- /dev/null +++ b/0.7/sdk/reference/api/datasources/datasource/index.html @@ -0,0 +1,2673 @@ + + + + + + + + + + + + + + + + + + + + + + DataSource - YData Fabric + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ + + + + + +
+ + +
+ +
+ + + + + + + + + +
+
+ + + +
+
+
+ + + + + + + + +
+
+
+ + + + +
+
+ + + + +
+ + + + +
+

+ Bases: ModelFactoryMixin

+ + +

A DataSource represents a dataset +to be used by a Synthesizer as training data.

+ + + +

Parameters:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescriptionDefault
connector + Connector + +
+

Connector from which the datasource is created

+
+
+ required +
datatype + Optional[Union[DataSourceType, str]] + +
+

(optional) DataSource type

+
+
+ TABULAR +
name + Optional[str] + +
+

(optional) DataSource name

+
+
+ None +
wait_for_metadata + bool + +
+

If True, wait until the metadata is fully calculated

+
+
+ True +
client + Client + +
+

(optional) Client to connect to the backend

+
+
+ None +
**config + +
+

Datasource specific configuration

+
+
+ {} +
+ + + +

Attributes:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescription
uid + UID + +
+

UID fo the datasource instance

+
+
datatype + DataSourceType + +
+

Data source type

+
+
status + Status + +
+

Status of the datasource

+
+
metadata + Metadata + +
+

Metadata associated to the datasource

+
+
+ +
+ Source code in ydata/sdk/datasources/datasource.py +
class DataSource(ModelFactoryMixin):
+    """A [`DataSource`][ydata.sdk.datasources.DataSource] represents a dataset
+    to be used by a Synthesizer as training data.
+
+    Arguments:
+        connector (Connector): Connector from which the datasource is created
+        datatype (Optional[Union[DataSourceType, str]]): (optional) DataSource type
+        name (Optional[str]): (optional) DataSource name
+        wait_for_metadata (bool): If `True`, wait until the metadata is fully calculated
+        client (Client): (optional) Client to connect to the backend
+        **config: Datasource specific configuration
+
+    Attributes:
+        uid (UID): UID fo the datasource instance
+        datatype (DataSourceType): Data source type
+        status (Status): Status of the datasource
+        metadata (Metadata): Metadata associated to the datasource
+    """
+
+    def __init__(self, connector: Connector, datatype: Optional[Union[DataSourceType, str]] = DataSourceType.TABULAR, name: Optional[str] = None, wait_for_metadata: bool = True, client: Optional[Client] = None, **config):
+        datasource_type = CONNECTOR_TO_DATASOURCE.get(connector.type)
+        self._init_common(client=client)
+        self._model: Optional[mDataSource] = self._create_model(
+            connector=connector, datasource_type=datasource_type, datatype=datatype, config=config, name=name, client=self._client)
+
+        if wait_for_metadata:
+            self._model = DataSource._wait_for_metadata(self)._model
+
+    @init_client
+    def _init_common(self, client: Optional[Client] = None):
+        self._client = client
+        self._logger = create_logger(__name__, level=LOG_LEVEL)
+
+    @property
+    def uid(self) -> UID:
+        return self._model.uid
+
+    @property
+    def datatype(self) -> DataSourceType:
+        return self._model.datatype
+
+    @property
+    def status(self) -> Status:
+        try:
+            self._model = self.get(self._model.uid, self._client)._model
+            return self._model.status
+        except Exception:  # noqa: PIE786
+            return Status.UNKNOWN
+
+    @property
+    def metadata(self) -> Metadata:
+        return self._model.metadata
+
+    @staticmethod
+    @init_client
+    def list(client: Optional[Client] = None) -> DataSourceList:
+        """List the  [`DataSource`][ydata.sdk.datasources.DataSource]
+        instances.
+
+        Arguments:
+            client (Client): (optional) Client to connect to the backend
+
+        Returns:
+            List of datasources
+        """
+        def __process_data(data: list) -> list:
+            to_del = ['metadata']
+            for e in data:
+                for k in to_del:
+                    e.pop(k, None)
+            return data
+
+        response = client.get('/datasource')
+        data: list = response.json()
+        data = __process_data(data)
+
+        return DataSourceList(data)
+
+    @staticmethod
+    @init_client
+    def get(uid: UID, client: Optional[Client] = None) -> "DataSource":
+        """Get an existing [`DataSource`][ydata.sdk.datasources.DataSource].
+
+        Arguments:
+            uid (UID): DataSource identifier
+            client (Client): (optional) Client to connect to the backend
+
+        Returns:
+            DataSource
+        """
+        response = client.get(f'/datasource/{uid}')
+        data: list = response.json()
+        datasource_type = CONNECTOR_TO_DATASOURCE.get(
+            ConnectorType(data['connector']['type']))
+        model = DataSource._model_from_api(data, datasource_type)
+        datasource = ModelFactoryMixin._init_from_model_data(DataSource, model)
+        return datasource
+
+    @classmethod
+    def create(cls, connector: Connector, datatype: Optional[Union[DataSourceType, str]] = DataSourceType.TABULAR, name: Optional[str] = None, wait_for_metadata: bool = True, client: Optional[Client] = None, **config) -> "DataSource":
+        """Create a new [`DataSource`][ydata.sdk.datasources.DataSource].
+
+        Arguments:
+            connector (Connector): Connector from which the datasource is created
+            datatype (Optional[Union[DataSourceType, str]]): (optional) DataSource type
+            name (Optional[str]): (optional) DataSource name
+            wait_for_metadata (bool): If `True`, wait until the metadata is fully calculated
+            client (Client): (optional) Client to connect to the backend
+            **config: Datasource specific configuration
+
+        Returns:
+            DataSource
+        """
+        datasource_type = CONNECTOR_TO_DATASOURCE.get(connector.type)
+        return cls._create(connector=connector, datasource_type=datasource_type, datatype=datatype, config=config, name=name, wait_for_metadata=wait_for_metadata, client=client)
+
+    @classmethod
+    def _create(cls, connector: Connector, datasource_type: Type[mDataSource], datatype: Optional[Union[DataSourceType, str]] = DataSourceType.TABULAR, config: Optional[Dict] = None, name: Optional[str] = None, wait_for_metadata: bool = True, client: Optional[Client] = None) -> "DataSource":
+        model = DataSource._create_model(
+            connector, datasource_type, datatype, config, name, client)
+        datasource = ModelFactoryMixin._init_from_model_data(DataSource, model)
+
+        if wait_for_metadata:
+            datasource._model = DataSource._wait_for_metadata(datasource)._model
+
+        return datasource
+
+    @classmethod
+    @init_client
+    def _create_model(cls, connector: Connector, datasource_type: Type[mDataSource], datatype: Optional[Union[DataSourceType, str]] = DataSourceType.TABULAR, config: Optional[Dict] = None, name: Optional[str] = None, client: Optional[Client] = None) -> mDataSource:
+        _name = name if name is not None else str(uuid4())
+        _config = config if config is not None else {}
+        payload = {
+            "name": _name,
+            "connector": {
+                "uid": connector.uid,
+                "type": connector.type.value
+            },
+            "dataType": datatype.value
+        }
+        if connector.type != ConnectorType.FILE:
+            _config = datasource_type(**config).to_payload()
+        payload.update(_config)
+        response = client.post('/datasource/', json=payload)
+        data: list = response.json()
+        return DataSource._model_from_api(data, datasource_type)
+
+    @staticmethod
+    def _wait_for_metadata(datasource):
+        logger = create_logger(__name__, level=LOG_LEVEL)
+        while datasource.status not in [Status.AVAILABLE, Status.FAILED, Status.UNAVAILABLE]:
+            logger.info(f'Calculating metadata [{datasource.status}]')
+            datasource = DataSource.get(uid=datasource.uid, client=datasource._client)
+            sleep(BACKOFF)
+        return datasource
+
+    @staticmethod
+    def _resolve_api_status(api_status: Dict) -> Status:
+        status = Status(api_status.get('state', Status.UNKNOWN.name))
+        validation = ValidationState(api_status.get('validation', {}).get(
+            'state', ValidationState.UNKNOWN.name))
+        if validation == ValidationState.FAILED:
+            status = Status.FAILED
+        return status
+
+    @staticmethod
+    def _model_from_api(data: Dict, datasource_type: Type[mDataSource]) -> mDataSource:
+        data['datatype'] = data.pop('dataType')
+        data['state'] = data['status']
+        data['status'] = DataSource._resolve_api_status(data['status'])
+        data = filter_dict(datasource_type, data)
+        model = datasource_type(**data)
+        return model
+
+    def __repr__(self):
+        return self._model.__repr__()
+
+
+ + + +
+ + + + + + + + + + +
+ + + + +

+ create(connector, datatype=DataSourceType.TABULAR, name=None, wait_for_metadata=True, client=None, **config) + + + classmethod + + +

+ + +
+ +

Create a new DataSource.

+ + + +

Parameters:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescriptionDefault
connector + Connector + +
+

Connector from which the datasource is created

+
+
+ required +
datatype + Optional[Union[DataSourceType, str]] + +
+

(optional) DataSource type

+
+
+ TABULAR +
name + Optional[str] + +
+

(optional) DataSource name

+
+
+ None +
wait_for_metadata + bool + +
+

If True, wait until the metadata is fully calculated

+
+
+ True +
client + Client + +
+

(optional) Client to connect to the backend

+
+
+ None +
**config + +
+

Datasource specific configuration

+
+
+ {} +
+ + + +

Returns:

+ + + + + + + + + + + + + +
TypeDescription
+ DataSource + +
+

DataSource

+
+
+ +
+ Source code in ydata/sdk/datasources/datasource.py +
@classmethod
+def create(cls, connector: Connector, datatype: Optional[Union[DataSourceType, str]] = DataSourceType.TABULAR, name: Optional[str] = None, wait_for_metadata: bool = True, client: Optional[Client] = None, **config) -> "DataSource":
+    """Create a new [`DataSource`][ydata.sdk.datasources.DataSource].
+
+    Arguments:
+        connector (Connector): Connector from which the datasource is created
+        datatype (Optional[Union[DataSourceType, str]]): (optional) DataSource type
+        name (Optional[str]): (optional) DataSource name
+        wait_for_metadata (bool): If `True`, wait until the metadata is fully calculated
+        client (Client): (optional) Client to connect to the backend
+        **config: Datasource specific configuration
+
+    Returns:
+        DataSource
+    """
+    datasource_type = CONNECTOR_TO_DATASOURCE.get(connector.type)
+    return cls._create(connector=connector, datasource_type=datasource_type, datatype=datatype, config=config, name=name, wait_for_metadata=wait_for_metadata, client=client)
+
+
+
+ +
+ + +
+ + + + +

+ get(uid, client=None) + + + staticmethod + + +

+ + +
+ +

Get an existing DataSource.

+ + + +

Parameters:

+ + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescriptionDefault
uid + UID + +
+

DataSource identifier

+
+
+ required +
client + Client + +
+

(optional) Client to connect to the backend

+
+
+ None +
+ + + +

Returns:

+ + + + + + + + + + + + + +
TypeDescription
+ DataSource + +
+

DataSource

+
+
+ +
+ Source code in ydata/sdk/datasources/datasource.py +
@staticmethod
+@init_client
+def get(uid: UID, client: Optional[Client] = None) -> "DataSource":
+    """Get an existing [`DataSource`][ydata.sdk.datasources.DataSource].
+
+    Arguments:
+        uid (UID): DataSource identifier
+        client (Client): (optional) Client to connect to the backend
+
+    Returns:
+        DataSource
+    """
+    response = client.get(f'/datasource/{uid}')
+    data: list = response.json()
+    datasource_type = CONNECTOR_TO_DATASOURCE.get(
+        ConnectorType(data['connector']['type']))
+    model = DataSource._model_from_api(data, datasource_type)
+    datasource = ModelFactoryMixin._init_from_model_data(DataSource, model)
+    return datasource
+
+
+
+ +
+ + +
+ + + + +

+ list(client=None) + + + staticmethod + + +

+ + +
+ +

List the DataSource +instances.

+ + + +

Parameters:

+ + + + + + + + + + + + + + + + + +
NameTypeDescriptionDefault
client + Client + +
+

(optional) Client to connect to the backend

+
+
+ None +
+ + + +

Returns:

+ + + + + + + + + + + + + +
TypeDescription
+ DataSourceList + +
+

List of datasources

+
+
+ +
+ Source code in ydata/sdk/datasources/datasource.py +
74
+75
+76
+77
+78
+79
+80
+81
+82
+83
+84
+85
+86
+87
+88
+89
+90
+91
+92
+93
+94
+95
+96
+97
@staticmethod
+@init_client
+def list(client: Optional[Client] = None) -> DataSourceList:
+    """List the  [`DataSource`][ydata.sdk.datasources.DataSource]
+    instances.
+
+    Arguments:
+        client (Client): (optional) Client to connect to the backend
+
+    Returns:
+        List of datasources
+    """
+    def __process_data(data: list) -> list:
+        to_del = ['metadata']
+        for e in data:
+            for k in to_del:
+                e.pop(k, None)
+        return data
+
+    response = client.get('/datasource')
+    data: list = response.json()
+    data = __process_data(data)
+
+    return DataSourceList(data)
+
+
+
+ +
+ + + +
+ +
+ +

Status

+ + +
+ + + + +
+

+ Bases: StringEnum

+ + +

Represent the status of a DataSource.

+ + + + +
+ + + + + + + +
+ + + + +

+ AVAILABLE = 'available' + + + class-attribute + instance-attribute + + +

+ + +
+ +

The DataSource is available and ready to be used.

+
+ +
+ +
+ + + + +

+ DELETED = 'deleted' + + + class-attribute + instance-attribute + + +

+ + +
+ +

The DataSource is to be deleted or has been deleted.

+
+ +
+ +
+ + + + +

+ FAILED = 'failed' + + + class-attribute + instance-attribute + + +

+ + +
+ +

The DataSource preparation or validation has failed.

+
+ +
+ +
+ + + + +

+ PREPARING = 'preparing' + + + class-attribute + instance-attribute + + +

+ + +
+ +

The DataSource is being prepared.

+
+ +
+ +
+ + + + +

+ UNAVAILABLE = 'unavailable' + + + class-attribute + instance-attribute + + +

+ + +
+ +

The DataSource is unavailable at the moment.

+
+ +
+ +
+ + + + +

+ UNKNOWN = 'unknown' + + + class-attribute + instance-attribute + + +

+ + +
+ +

The DataSource status could not be retrieved.

+
+ +
+ +
+ + + + +

+ VALIDATING = 'validating' + + + class-attribute + instance-attribute + + +

+ + +
+ +

The DataSource is being validated.

+
+ +
+ + + + + +
+ +
+ +

DataSourceType

+ + +
+ + + + +
+

+ Bases: StringEnum

+ + + + + +
+ + + + + + + +
+ + + + +

+ TABULAR = 'tabular' + + + class-attribute + instance-attribute + + +

+ + +
+ +

The DataSource is tabular (i.e. it does not have a temporal dimension).

+
+ +
+ +
+ + + + +

+ TIMESERIES = 'timeseries' + + + class-attribute + instance-attribute + + +

+ + +
+ +

The DataSource has a temporal dimension.

+
+ +
+ + + + + +
+ +
+ +
+ + + + + + +
+
+ + + + +
+ + + +
+ + + +
+
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/0.7/sdk/reference/api/datasources/metadata/index.html b/0.7/sdk/reference/api/datasources/metadata/index.html new file mode 100644 index 00000000..53e141be --- /dev/null +++ b/0.7/sdk/reference/api/datasources/metadata/index.html @@ -0,0 +1,1344 @@ + + + + + + + + + + + + + + + + + + + + + + Metadata - YData Fabric + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ + + + + + +
+ + +
+ +
+ + + + + + + + + +
+
+ + + +
+
+
+ + + + + + + + +
+
+
+ + + + +
+
+ + + + +

Metadata

+ +
+ + + + +
+

+ Bases: BaseModel

+ + +

The Metadata object contains descriptive information about a.

+

DataSource

+ + + +

Attributes:

+ + + + + + + + + + + + + + + +
NameTypeDescription
columns + List[Column] + +
+

columns information

+
+
+ + + + +
+ + + + + + + + + + + +
+ +
+ +
+ + + + + + +
+
+ + + + +
+ + + +
+ + + +
+
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/0.7/sdk/reference/api/index.html b/0.7/sdk/reference/api/index.html new file mode 100644 index 00000000..eab298cf --- /dev/null +++ b/0.7/sdk/reference/api/index.html @@ -0,0 +1,1194 @@ + + + + + + + + + + + + + + + + + + Index - YData Fabric + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+
+ +
+ + + + + + +
+ + +
+ +
+ + + + + + + + + +
+
+ + + +
+
+
+ + + + + + + + +
+
+
+ + + + +
+
+ + + + +

Index

+ + + + + + + + +
+
+ + + + +
+ + + +
+ +
+ + + + +
+ +
+
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/0.7/sdk/reference/api/synthesizers/base/index.html b/0.7/sdk/reference/api/synthesizers/base/index.html new file mode 100644 index 00000000..fb788e96 --- /dev/null +++ b/0.7/sdk/reference/api/synthesizers/base/index.html @@ -0,0 +1,2859 @@ + + + + + + + + + + + + + + + + + + + + + + Synthesizer - YData Fabric + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ + + + + + +
+ + +
+ +
+ + + + + + + + + +
+
+ + + +
+
+
+ + + + + + + + +
+
+
+ + + + +
+
+ + + + +
+ + + + +
+

+ Bases: ABC, ModelFactoryMixin

+ + +

Main synthesizer class.

+

This class cannot be directly instanciated because of the specificities between RegularSynthesizer and TimeSeriesSynthesizer sample methods.

+

Methods

+
    +
  • fit: train a synthesizer instance.
  • +
  • sample: request synthetic data.
  • +
  • status: current status of the synthesizer instance.
  • +
+ +
+ Note +

The synthesizer instance is created in the backend only when the fit method is called.

+
+ + +

Parameters:

+ + + + + + + + + + + + + + + + + +
NameTypeDescriptionDefault
client + Client + +
+

(optional) Client to connect to the backend

+
+
+ None +
+ +
+ Source code in ydata/sdk/synthesizers/synthesizer.py +
 35
+ 36
+ 37
+ 38
+ 39
+ 40
+ 41
+ 42
+ 43
+ 44
+ 45
+ 46
+ 47
+ 48
+ 49
+ 50
+ 51
+ 52
+ 53
+ 54
+ 55
+ 56
+ 57
+ 58
+ 59
+ 60
+ 61
+ 62
+ 63
+ 64
+ 65
+ 66
+ 67
+ 68
+ 69
+ 70
+ 71
+ 72
+ 73
+ 74
+ 75
+ 76
+ 77
+ 78
+ 79
+ 80
+ 81
+ 82
+ 83
+ 84
+ 85
+ 86
+ 87
+ 88
+ 89
+ 90
+ 91
+ 92
+ 93
+ 94
+ 95
+ 96
+ 97
+ 98
+ 99
+100
+101
+102
+103
+104
+105
+106
+107
+108
+109
+110
+111
+112
+113
+114
+115
+116
+117
+118
+119
+120
+121
+122
+123
+124
+125
+126
+127
+128
+129
+130
+131
+132
+133
+134
+135
+136
+137
+138
+139
+140
+141
+142
+143
+144
+145
+146
+147
+148
+149
+150
+151
+152
+153
+154
+155
+156
+157
+158
+159
+160
+161
+162
+163
+164
+165
+166
+167
+168
+169
+170
+171
+172
+173
+174
+175
+176
+177
+178
+179
+180
+181
+182
+183
+184
+185
+186
+187
+188
+189
+190
+191
+192
+193
+194
+195
+196
+197
+198
+199
+200
+201
+202
+203
+204
+205
+206
+207
+208
+209
+210
+211
+212
+213
+214
+215
+216
+217
+218
+219
+220
+221
+222
+223
+224
+225
+226
+227
+228
+229
+230
+231
+232
+233
+234
+235
+236
+237
+238
+239
+240
+241
+242
+243
+244
+245
+246
+247
+248
+249
+250
+251
+252
+253
+254
+255
+256
+257
+258
+259
+260
+261
+262
+263
+264
+265
+266
+267
+268
+269
+270
+271
+272
+273
+274
+275
+276
+277
+278
+279
+280
+281
+282
+283
+284
+285
+286
+287
+288
+289
+290
+291
+292
+293
+294
+295
+296
+297
+298
+299
+300
+301
+302
+303
+304
+305
+306
+307
+308
+309
+310
+311
+312
+313
+314
+315
+316
+317
+318
+319
+320
+321
+322
+323
+324
+325
+326
+327
+328
+329
+330
+331
+332
+333
+334
+335
+336
+337
+338
+339
+340
+341
+342
+343
+344
+345
+346
+347
+348
+349
+350
+351
+352
+353
+354
+355
+356
+357
+358
+359
+360
+361
+362
+363
+364
+365
+366
+367
+368
+369
+370
+371
+372
+373
+374
+375
+376
+377
+378
+379
+380
+381
+382
+383
+384
+385
+386
+387
+388
+389
+390
@typechecked
+class BaseSynthesizer(ABC, ModelFactoryMixin):
+    """Main synthesizer class.
+
+    This class cannot be directly instanciated because of the specificities between [`RegularSynthesizer`][ydata.sdk.synthesizers.RegularSynthesizer] and [`TimeSeriesSynthesizer`][ydata.sdk.synthesizers.TimeSeriesSynthesizer] `sample` methods.
+
+    Methods
+    -------
+    - `fit`: train a synthesizer instance.
+    - `sample`: request synthetic data.
+    - `status`: current status of the synthesizer instance.
+
+    Note:
+            The synthesizer instance is created in the backend only when the `fit` method is called.
+
+    Arguments:
+        client (Client): (optional) Client to connect to the backend
+    """
+
+    def __init__(self, uid: UID | None = None, name: str | None = None, project: Project | None = None, client: Client | None = None):
+        self._init_common(client=client)
+        self._model = mSynthesizer(uid=uid, name=name or str(
+            uuid4())) if uid or project else None
+        self.__project = project
+
+    @init_client
+    def _init_common(self, client: Optional[Client] = None):
+        self._client = client
+        self._logger = create_logger(__name__, level=LOG_LEVEL)
+
+    def fit(self, X: Union[DataSource, pdDataFrame],
+            privacy_level: PrivacyLevel = PrivacyLevel.HIGH_FIDELITY,
+            datatype: Optional[Union[DataSourceType, str]] = None,
+            sortbykey: Optional[Union[str, List[str]]] = None,
+            entities: Optional[Union[str, List[str]]] = None,
+            generate_cols: Optional[List[str]] = None,
+            exclude_cols: Optional[List[str]] = None,
+            dtypes: Optional[Dict[str, Union[str, DataType]]] = None,
+            target: Optional[str] = None,
+            anonymize: Optional[dict] = None,
+            condition_on: Optional[List[str]] = None) -> None:
+        """Fit the synthesizer.
+
+        The synthesizer accepts as training dataset either a pandas [`DataFrame`][pandas.DataFrame] directly or a YData [`DataSource`][ydata.sdk.datasources.DataSource].
+        When the training dataset is a pandas [`DataFrame`][pandas.DataFrame], the argument `datatype` is required as it cannot be deduced.
+
+        The argument`sortbykey` is mandatory for [`TimeSeries`][ydata.sdk.datasources.DataSourceType.TIMESERIES].
+
+        By default, if `generate_cols` or `exclude_cols` are not specified, all columns are generated by the synthesizer.
+        The argument `exclude_cols` has precedence over `generate_cols`, i.e. a column `col` will not be generated if it is in both list.
+
+        Arguments:
+            X (Union[DataSource, pandas.DataFrame]): Training dataset
+            privacy_level (PrivacyLevel): Synthesizer privacy level (defaults to high fidelity)
+            datatype (Optional[Union[DataSourceType, str]]): (optional) Dataset datatype - required if `X` is a [`pandas.DataFrame`][pandas.DataFrame]
+            sortbykey (Union[str, List[str]]): (optional) column(s) to use to sort timeseries datasets
+            entities (Union[str, List[str]]): (optional) columns representing entities ID
+            generate_cols (List[str]): (optional) columns that should be synthesized
+            exclude_cols (List[str]): (optional) columns that should not be synthesized
+            dtypes (Dict[str, Union[str, DataType]]): (optional) datatype mapping that will overwrite the datasource metadata column datatypes
+            target (Optional[str]): (optional) Target for the dataset
+            name (Optional[str]): (optional) Synthesizer instance name
+            anonymize (Optional[str]): (optional) fields to anonymize and the anonymization strategy
+            condition_on: (Optional[List[str]]): (optional) list of features to condition upon
+        """
+        if self._is_initialized():
+            raise AlreadyFittedError()
+
+        _datatype = DataSourceType(datatype) if isinstance(
+            X, pdDataFrame) else DataSourceType(X.datatype)
+
+        dataset_attrs = self._init_datasource_attributes(
+            sortbykey, entities, generate_cols, exclude_cols, dtypes)
+        self._validate_datasource_attributes(X, dataset_attrs, _datatype, target)
+
+        # If the training data is a pandas dataframe, we first need to create a data source and then the instance
+        if isinstance(X, pdDataFrame):
+            if X.empty:
+                raise EmptyDataError("The DataFrame is empty")
+            _X = LocalDataSource(source=X, datatype=_datatype, client=self._client)
+        else:
+            if datatype != _datatype:
+                warn("When the training data is a DataSource, the argument `datatype` is ignored.",
+                     DataSourceTypeWarning)
+            _X = X
+
+        if _X.status != dsStatus.AVAILABLE:
+            raise DataSourceNotAvailableError(
+                f"The datasource '{_X.uid}' is not available (status = {_X.status.value})")
+
+        if isinstance(dataset_attrs, dict):
+            dataset_attrs = DataSourceAttrs(**dataset_attrs)
+
+        self._fit_from_datasource(
+            X=_X, dataset_attrs=dataset_attrs, target=target,
+            anonymize=anonymize, privacy_level=privacy_level, condition_on=condition_on)
+
+    @staticmethod
+    def _init_datasource_attributes(
+            sortbykey: Optional[Union[str, List[str]]],
+            entities: Optional[Union[str, List[str]]],
+            generate_cols: Optional[List[str]],
+            exclude_cols: Optional[List[str]],
+            dtypes: Optional[Dict[str, Union[str, DataType]]]) -> DataSourceAttrs:
+        dataset_attrs = {
+            'sortbykey': sortbykey if sortbykey is not None else [],
+            'entities': entities if entities is not None else [],
+            'generate_cols': generate_cols if generate_cols is not None else [],
+            'exclude_cols': exclude_cols if exclude_cols is not None else [],
+            'dtypes': {k: DataType(v) for k, v in dtypes.items()} if dtypes is not None else {}
+        }
+        return DataSourceAttrs(**dataset_attrs)
+
+    @staticmethod
+    def _validate_datasource_attributes(X: Union[DataSource, pdDataFrame], dataset_attrs: DataSourceAttrs, datatype: DataSourceType, target: Optional[str]):
+        columns = []
+        if isinstance(X, pdDataFrame):
+            columns = X.columns
+            if datatype is None:
+                raise DataTypeMissingError(
+                    "Argument `datatype` is mandatory for pandas.DataFrame training data")
+            datatype = DataSourceType(datatype)
+        else:
+            columns = [c.name for c in X.metadata.columns]
+
+        if target is not None and target not in columns:
+            raise DataSourceAttrsError(
+                "Invalid target: column '{target}' does not exist")
+
+        if datatype == DataSourceType.TIMESERIES:
+            if not dataset_attrs.sortbykey:
+                raise DataSourceAttrsError(
+                    "The argument `sortbykey` is mandatory for timeseries datasource.")
+
+        invalid_fields = {}
+        for field, v in dataset_attrs.dict().items():
+            field_columns = v if field != 'dtypes' else v.keys()
+            not_in_cols = [c for c in field_columns if c not in columns]
+            if len(not_in_cols) > 0:
+                invalid_fields[field] = not_in_cols
+
+        if len(invalid_fields) > 0:
+            error_msgs = ["\t- Field '{}': columns {} do not exist".format(
+                f, ', '.join(v)) for f, v in invalid_fields.items()]
+            raise DataSourceAttrsError(
+                "The dataset attributes are invalid:\n {}".format('\n'.join(error_msgs)))
+
+    @staticmethod
+    def _metadata_to_payload(
+        datatype: DataSourceType, ds_metadata: Metadata,
+        dataset_attrs: Optional[DataSourceAttrs] = None, target: str | None = None
+    ) -> dict:
+        """Transform a the metadata and dataset attributes into a valid
+        payload.
+
+        Arguments:
+            datatype (DataSourceType): datasource type
+            ds_metadata (Metadata): datasource metadata object
+            dataset_attrs ( Optional[DataSourceAttrs] ): (optional) Dataset attributes
+            target (Optional[str]): (optional) target column name
+
+        Returns:
+            metadata payload dictionary
+        """
+
+        columns = [
+            {
+                'name': c.name,
+                'generation': True and c.name not in dataset_attrs.exclude_cols,
+                'dataType': DataType(dataset_attrs.dtypes[c.name]).value if c.name in dataset_attrs.dtypes else c.datatype,
+                'varType': c.vartype,
+            }
+            for c in ds_metadata.columns]
+
+        metadata = {
+            'columns': columns,
+            'target': target
+        }
+
+        if dataset_attrs is not None:
+            if datatype == DataSourceType.TIMESERIES:
+                metadata['sortBy'] = [c for c in dataset_attrs.sortbykey]
+                metadata['entity'] = [c for c in dataset_attrs.entities]
+
+        return metadata
+
+    def _fit_from_datasource(
+        self,
+        X: DataSource,
+        privacy_level: PrivacyLevel = PrivacyLevel.HIGH_FIDELITY,
+        dataset_attrs: Optional[DataSourceAttrs] = None,
+        target: Optional[str] = None,
+        anonymize: Optional[dict] = None,
+        condition_on: Optional[List[str]] = None
+    ) -> None:
+        metadata = self._metadata_to_payload(
+            DataSourceType(X.datatype), X.metadata, dataset_attrs, target)
+        payload = {
+            'name': self._model.name,
+            'dataSourceUID': X.uid,
+            'metadata': metadata,
+            'extraData': {},
+            'privacyLevel': privacy_level.value
+        }
+        if anonymize is not None:
+            payload["extraData"]["anonymize"] = anonymize
+        if condition_on is not None:
+            payload["extraData"]["condition_on"] = condition_on
+
+        response = self._client.post(
+            '/synthesizer/', json=payload, project=self.__project)
+        data: list = response.json()
+        self._model, _ = self._model_from_api(X.datatype, data)
+        while self.status not in [Status.READY, Status.FAILED]:
+            self._logger.info('Training the synthesizer...')
+            sleep(BACKOFF)
+
+        if self.status == Status.FAILED:
+            raise FittingError('Could not train the synthesizer')
+
+    @staticmethod
+    def _model_from_api(datatype: str, data: Dict) -> Tuple[mSynthesizer, Type["BaseSynthesizer"]]:
+        from ydata.sdk.synthesizers._models.synthesizer_map import TYPE_TO_CLASS
+        synth_cls = TYPE_TO_CLASS.get(SynthesizerType(datatype).value)
+        data['status'] = synth_cls._resolve_api_status(data['status'])
+        data = filter_dict(mSynthesizer, data)
+        return mSynthesizer(**data), synth_cls
+
+    @abstractmethod
+    def sample(self) -> pdDataFrame:
+        """Abstract method to sample from a synthesizer."""
+
+    def _sample(self, payload: Dict) -> pdDataFrame:
+        """Sample from a synthesizer.
+
+        Arguments:
+            payload (dict): payload configuring the sample request
+
+        Returns:
+            pandas `DataFrame`
+        """
+        response = self._client.post(
+            f"/synthesizer/{self.uid}/sample", json=payload, project=self.__project)
+
+        data: Dict = response.json()
+        sample_uid = data.get('uid')
+        sample_status = None
+        while sample_status not in ['finished', 'failed']:
+            self._logger.info('Sampling from the synthesizer...')
+            response = self._client.get(
+                f'/synthesizer/{self.uid}/history', project=self.__project)
+            history: Dict = response.json()
+            sample_data = next((s for s in history if s.get('uid') == sample_uid), None)
+            sample_status = sample_data.get('status', {}).get('state')
+            sleep(BACKOFF)
+
+        response = self._client.get_static_file(
+            f'/synthesizer/{self.uid}/sample/{sample_uid}/sample.csv', project=self.__project)
+        data = StringIO(response.content.decode())
+        return read_csv(data)
+
+    @property
+    def uid(self) -> UID:
+        """Get the status of a synthesizer instance.
+
+        Returns:
+            Synthesizer status
+        """
+        if not self._is_initialized():
+            return Status.NOT_INITIALIZED
+
+        return self._model.uid
+
+    @property
+    def status(self) -> Status:
+        """Get the status of a synthesizer instance.
+
+        Returns:
+            Synthesizer status
+        """
+        if not self._is_initialized():
+            return Status.NOT_INITIALIZED
+
+        try:
+            self = self.get(self._model.uid, self._client)
+            return self._model.status
+        except Exception:  # noqa: PIE786
+            return Status.UNKNOWN
+
+    def get(self):
+        assert self._is_initialized() and self._model.uid, InputError(
+            "Please provide the synthesizer `uid`")
+
+        response = self._client.get(f'/synthesizer/{self.uid}', project=self.__project)
+        data = filter_dict(mSynthesizer, response.json())
+        self._model = mSynthesizer(**data)
+
+        return self
+
+    @staticmethod
+    @init_client
+    def list(client: Optional[Client] = None) -> SynthesizersList:
+        """List the synthesizer instances.
+
+        Arguments:
+            client (Client): (optional) Client to connect to the backend
+
+        Returns:
+            List of synthesizers
+        """
+        def __process_data(data: list) -> list:
+            to_del = ['metadata', 'report', 'mode']
+            for e in data:
+                for k in to_del:
+                    e.pop(k, None)
+            return data
+
+        response = client.get('/synthesizer')
+        data: list = response.json()
+        data = __process_data(data)
+
+        return SynthesizersList(data)
+
+    def _is_initialized(self) -> bool:
+        """Determine if a synthesizer is instanciated or not.
+
+        Returns:
+            True if the synthesizer is instanciated
+        """
+        return self._model is not None
+
+    @staticmethod
+    def _resolve_api_status(api_status: Dict) -> Status:
+        """Determine the status of the Synthesizer.
+
+        The status of the synthesizer instance is determined by the state of
+        its different components.
+
+        Arguments:
+            api_status (dict): json from the endpoint GET /synthesizer
+
+        Returns:
+            Synthesizer Status
+        """
+        status = Status(api_status.get('state', Status.UNKNOWN.name))
+        if status == Status.PREPARE:
+            if PrepareState(api_status.get('prepare', {}).get(
+                    'state', PrepareState.UNKNOWN.name)) == PrepareState.FAILED:
+                return Status.FAILED
+        elif status == Status.TRAIN:
+            if TrainingState(api_status.get('training', {}).get(
+                    'state', TrainingState.UNKNOWN.name)) == TrainingState.FAILED:
+                return Status.FAILED
+        elif status == Status.REPORT:
+            return Status.READY
+        return status
+
+
+ + + +
+ + + + + + + +
+ + + + +

+ status: Status + + + property + + +

+ + +
+ +

Get the status of a synthesizer instance.

+ + + +

Returns:

+ + + + + + + + + + + + + +
TypeDescription
+ Status + +
+

Synthesizer status

+
+
+
+ +
+ +
+ + + + +

+ uid: UID + + + property + + +

+ + +
+ +

Get the status of a synthesizer instance.

+ + + +

Returns:

+ + + + + + + + + + + + + +
TypeDescription
+ UID + +
+

Synthesizer status

+
+
+
+ +
+ + + + +
+ + + + +

+ fit(X, privacy_level=PrivacyLevel.HIGH_FIDELITY, datatype=None, sortbykey=None, entities=None, generate_cols=None, exclude_cols=None, dtypes=None, target=None, anonymize=None, condition_on=None) + +

+ + +
+ +

Fit the synthesizer.

+

The synthesizer accepts as training dataset either a pandas DataFrame directly or a YData DataSource. +When the training dataset is a pandas DataFrame, the argument datatype is required as it cannot be deduced.

+

The argumentsortbykey is mandatory for TimeSeries.

+

By default, if generate_cols or exclude_cols are not specified, all columns are generated by the synthesizer. +The argument exclude_cols has precedence over generate_cols, i.e. a column col will not be generated if it is in both list.

+ + + +

Parameters:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescriptionDefault
X + Union[DataSource, DataFrame] + +
+

Training dataset

+
+
+ required +
privacy_level + PrivacyLevel + +
+

Synthesizer privacy level (defaults to high fidelity)

+
+
+ HIGH_FIDELITY +
datatype + Optional[Union[DataSourceType, str]] + +
+

(optional) Dataset datatype - required if X is a pandas.DataFrame

+
+
+ None +
sortbykey + Union[str, List[str]] + +
+

(optional) column(s) to use to sort timeseries datasets

+
+
+ None +
entities + Union[str, List[str]] + +
+

(optional) columns representing entities ID

+
+
+ None +
generate_cols + List[str] + +
+

(optional) columns that should be synthesized

+
+
+ None +
exclude_cols + List[str] + +
+

(optional) columns that should not be synthesized

+
+
+ None +
dtypes + Dict[str, Union[str, DataType]] + +
+

(optional) datatype mapping that will overwrite the datasource metadata column datatypes

+
+
+ None +
target + Optional[str] + +
+

(optional) Target for the dataset

+
+
+ None +
name + Optional[str] + +
+

(optional) Synthesizer instance name

+
+
+ required +
anonymize + Optional[str] + +
+

(optional) fields to anonymize and the anonymization strategy

+
+
+ None +
condition_on + Optional[List[str]] + +
+

(Optional[List[str]]): (optional) list of features to condition upon

+
+
+ None +
+ +
+ Source code in ydata/sdk/synthesizers/synthesizer.py +
def fit(self, X: Union[DataSource, pdDataFrame],
+        privacy_level: PrivacyLevel = PrivacyLevel.HIGH_FIDELITY,
+        datatype: Optional[Union[DataSourceType, str]] = None,
+        sortbykey: Optional[Union[str, List[str]]] = None,
+        entities: Optional[Union[str, List[str]]] = None,
+        generate_cols: Optional[List[str]] = None,
+        exclude_cols: Optional[List[str]] = None,
+        dtypes: Optional[Dict[str, Union[str, DataType]]] = None,
+        target: Optional[str] = None,
+        anonymize: Optional[dict] = None,
+        condition_on: Optional[List[str]] = None) -> None:
+    """Fit the synthesizer.
+
+    The synthesizer accepts as training dataset either a pandas [`DataFrame`][pandas.DataFrame] directly or a YData [`DataSource`][ydata.sdk.datasources.DataSource].
+    When the training dataset is a pandas [`DataFrame`][pandas.DataFrame], the argument `datatype` is required as it cannot be deduced.
+
+    The argument`sortbykey` is mandatory for [`TimeSeries`][ydata.sdk.datasources.DataSourceType.TIMESERIES].
+
+    By default, if `generate_cols` or `exclude_cols` are not specified, all columns are generated by the synthesizer.
+    The argument `exclude_cols` has precedence over `generate_cols`, i.e. a column `col` will not be generated if it is in both list.
+
+    Arguments:
+        X (Union[DataSource, pandas.DataFrame]): Training dataset
+        privacy_level (PrivacyLevel): Synthesizer privacy level (defaults to high fidelity)
+        datatype (Optional[Union[DataSourceType, str]]): (optional) Dataset datatype - required if `X` is a [`pandas.DataFrame`][pandas.DataFrame]
+        sortbykey (Union[str, List[str]]): (optional) column(s) to use to sort timeseries datasets
+        entities (Union[str, List[str]]): (optional) columns representing entities ID
+        generate_cols (List[str]): (optional) columns that should be synthesized
+        exclude_cols (List[str]): (optional) columns that should not be synthesized
+        dtypes (Dict[str, Union[str, DataType]]): (optional) datatype mapping that will overwrite the datasource metadata column datatypes
+        target (Optional[str]): (optional) Target for the dataset
+        name (Optional[str]): (optional) Synthesizer instance name
+        anonymize (Optional[str]): (optional) fields to anonymize and the anonymization strategy
+        condition_on: (Optional[List[str]]): (optional) list of features to condition upon
+    """
+    if self._is_initialized():
+        raise AlreadyFittedError()
+
+    _datatype = DataSourceType(datatype) if isinstance(
+        X, pdDataFrame) else DataSourceType(X.datatype)
+
+    dataset_attrs = self._init_datasource_attributes(
+        sortbykey, entities, generate_cols, exclude_cols, dtypes)
+    self._validate_datasource_attributes(X, dataset_attrs, _datatype, target)
+
+    # If the training data is a pandas dataframe, we first need to create a data source and then the instance
+    if isinstance(X, pdDataFrame):
+        if X.empty:
+            raise EmptyDataError("The DataFrame is empty")
+        _X = LocalDataSource(source=X, datatype=_datatype, client=self._client)
+    else:
+        if datatype != _datatype:
+            warn("When the training data is a DataSource, the argument `datatype` is ignored.",
+                 DataSourceTypeWarning)
+        _X = X
+
+    if _X.status != dsStatus.AVAILABLE:
+        raise DataSourceNotAvailableError(
+            f"The datasource '{_X.uid}' is not available (status = {_X.status.value})")
+
+    if isinstance(dataset_attrs, dict):
+        dataset_attrs = DataSourceAttrs(**dataset_attrs)
+
+    self._fit_from_datasource(
+        X=_X, dataset_attrs=dataset_attrs, target=target,
+        anonymize=anonymize, privacy_level=privacy_level, condition_on=condition_on)
+
+
+
+ +
+ + +
+ + + + +

+ list(client=None) + + + staticmethod + + +

+ + +
+ +

List the synthesizer instances.

+ + + +

Parameters:

+ + + + + + + + + + + + + + + + + +
NameTypeDescriptionDefault
client + Client + +
+

(optional) Client to connect to the backend

+
+
+ None +
+ + + +

Returns:

+ + + + + + + + + + + + + +
TypeDescription
+ SynthesizersList + +
+

List of synthesizers

+
+
+ +
+ Source code in ydata/sdk/synthesizers/synthesizer.py +
@staticmethod
+@init_client
+def list(client: Optional[Client] = None) -> SynthesizersList:
+    """List the synthesizer instances.
+
+    Arguments:
+        client (Client): (optional) Client to connect to the backend
+
+    Returns:
+        List of synthesizers
+    """
+    def __process_data(data: list) -> list:
+        to_del = ['metadata', 'report', 'mode']
+        for e in data:
+            for k in to_del:
+                e.pop(k, None)
+        return data
+
+    response = client.get('/synthesizer')
+    data: list = response.json()
+    data = __process_data(data)
+
+    return SynthesizersList(data)
+
+
+
+ +
+ + +
+ + + + +

+ sample() + + + abstractmethod + + +

+ + +
+ +

Abstract method to sample from a synthesizer.

+ +
+ Source code in ydata/sdk/synthesizers/synthesizer.py +
@abstractmethod
+def sample(self) -> pdDataFrame:
+    """Abstract method to sample from a synthesizer."""
+
+
+
+ +
+ + + +
+ +
+ +

PrivacyLevel

+ + +
+ + + + +
+

+ Bases: StringEnum

+ + +

Privacy level exposed to the end-user.

+ + + + +
+ + + + + + + +
+ + + + +

+ BALANCED_PRIVACY_FIDELITY = 'BALANCED_PRIVACY_FIDELITY' + + + class-attribute + instance-attribute + + +

+ + +
+ +

Balanced privacy/fidelity

+
+ +
+ +
+ + + + +

+ HIGH_FIDELITY = 'HIGH_FIDELITY' + + + class-attribute + instance-attribute + + +

+ + +
+ +

High fidelity

+
+ +
+ +
+ + + + +

+ HIGH_PRIVACY = 'HIGH_PRIVACY' + + + class-attribute + instance-attribute + + +

+ + +
+ +

High privacy

+
+ +
+ + + + + +
+ +
+ +
+ + + + + + +
+
+ + + + +
+ + + +
+ + + +
+
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/0.7/sdk/reference/api/synthesizers/regular/index.html b/0.7/sdk/reference/api/synthesizers/regular/index.html new file mode 100644 index 00000000..9609ade7 --- /dev/null +++ b/0.7/sdk/reference/api/synthesizers/regular/index.html @@ -0,0 +1,1960 @@ + + + + + + + + + + + + + + + + + + + + + + Regular - YData Fabric + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ + + + + + +
+ + +
+ +
+ + + + + + + + + +
+
+ + + +
+
+
+ + + + + + + + +
+
+
+ + + + +
+
+ + + + +
+ + + + +
+

+ Bases: BaseSynthesizer

+ + +
+ Source code in ydata/sdk/synthesizers/regular.py +
13
+14
+15
+16
+17
+18
+19
+20
+21
+22
+23
+24
+25
+26
+27
+28
+29
+30
+31
+32
+33
+34
+35
+36
+37
+38
+39
+40
+41
+42
+43
+44
+45
+46
+47
+48
+49
+50
+51
+52
+53
+54
+55
+56
+57
+58
+59
+60
+61
+62
+63
+64
+65
+66
+67
+68
+69
+70
class RegularSynthesizer(BaseSynthesizer):
+
+    def sample(self, n_samples: int = 1, condition_on: Optional[dict] = None) -> pdDataFrame:
+        """Sample from a [`RegularSynthesizer`][ydata.sdk.synthesizers.RegularSynthesizer]
+        instance.
+
+        Arguments:
+            n_samples (int): number of rows in the sample
+            condition_on: (Optional[dict]): (optional) conditional sampling parameters
+
+        Returns:
+            synthetic data
+        """
+        if n_samples < 1:
+            raise InputError("Parameter 'n_samples' must be greater than 0")
+
+        payload = {"numberOfRecords": n_samples}
+        if condition_on is not None:
+            payload["extraData"] = {
+                "condition_on": condition_on
+            }
+        return self._sample(payload=payload)
+
+    def fit(self, X: Union[DataSource, pdDataFrame],
+            privacy_level: PrivacyLevel = PrivacyLevel.HIGH_FIDELITY,
+            entities: Optional[Union[str, List[str]]] = None,
+            generate_cols: Optional[List[str]] = None,
+            exclude_cols: Optional[List[str]] = None,
+            dtypes: Optional[Dict[str, Union[str, DataType]]] = None,
+            target: Optional[str] = None,
+            anonymize: Optional[dict] = None,
+            condition_on: Optional[List[str]] = None) -> None:
+        """Fit the synthesizer.
+
+        The synthesizer accepts as training dataset either a pandas [`DataFrame`][pandas.DataFrame] directly or a YData [`DataSource`][ydata.sdk.datasources.DataSource].
+
+        Arguments:
+            X (Union[DataSource, pandas.DataFrame]): Training dataset
+            privacy_level (PrivacyLevel): Synthesizer privacy level (defaults to high fidelity)
+            entities (Union[str, List[str]]): (optional) columns representing entities ID
+            generate_cols (List[str]): (optional) columns that should be synthesized
+            exclude_cols (List[str]): (optional) columns that should not be synthesized
+            dtypes (Dict[str, Union[str, DataType]]): (optional) datatype mapping that will overwrite the datasource metadata column datatypes
+            target (Optional[str]): (optional) Target column
+            name (Optional[str]): (optional) Synthesizer instance name
+            anonymize (Optional[str]): (optional) fields to anonymize and the anonymization strategy
+            condition_on: (Optional[List[str]]): (optional) list of features to condition upon
+        """
+        BaseSynthesizer.fit(self, X=X, datatype=DataSourceType.TABULAR, entities=entities,
+                            generate_cols=generate_cols, exclude_cols=exclude_cols, dtypes=dtypes,
+                            target=target, anonymize=anonymize, privacy_level=privacy_level,
+                            condition_on=condition_on)
+
+    def __repr__(self):
+        if self._model is not None:
+            return self._model.__repr__()
+        else:
+            return "RegularSynthesizer(Not Initialized)"
+
+
+ + + +
+ + + + + + + + + + +
+ + + + +

+ fit(X, privacy_level=PrivacyLevel.HIGH_FIDELITY, entities=None, generate_cols=None, exclude_cols=None, dtypes=None, target=None, anonymize=None, condition_on=None) + +

+ + +
+ +

Fit the synthesizer.

+

The synthesizer accepts as training dataset either a pandas DataFrame directly or a YData DataSource.

+ + + +

Parameters:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescriptionDefault
X + Union[DataSource, DataFrame] + +
+

Training dataset

+
+
+ required +
privacy_level + PrivacyLevel + +
+

Synthesizer privacy level (defaults to high fidelity)

+
+
+ HIGH_FIDELITY +
entities + Union[str, List[str]] + +
+

(optional) columns representing entities ID

+
+
+ None +
generate_cols + List[str] + +
+

(optional) columns that should be synthesized

+
+
+ None +
exclude_cols + List[str] + +
+

(optional) columns that should not be synthesized

+
+
+ None +
dtypes + Dict[str, Union[str, DataType]] + +
+

(optional) datatype mapping that will overwrite the datasource metadata column datatypes

+
+
+ None +
target + Optional[str] + +
+

(optional) Target column

+
+
+ None +
name + Optional[str] + +
+

(optional) Synthesizer instance name

+
+
+ required +
anonymize + Optional[str] + +
+

(optional) fields to anonymize and the anonymization strategy

+
+
+ None +
condition_on + Optional[List[str]] + +
+

(Optional[List[str]]): (optional) list of features to condition upon

+
+
+ None +
+ +
+ Source code in ydata/sdk/synthesizers/regular.py +
36
+37
+38
+39
+40
+41
+42
+43
+44
+45
+46
+47
+48
+49
+50
+51
+52
+53
+54
+55
+56
+57
+58
+59
+60
+61
+62
+63
+64
def fit(self, X: Union[DataSource, pdDataFrame],
+        privacy_level: PrivacyLevel = PrivacyLevel.HIGH_FIDELITY,
+        entities: Optional[Union[str, List[str]]] = None,
+        generate_cols: Optional[List[str]] = None,
+        exclude_cols: Optional[List[str]] = None,
+        dtypes: Optional[Dict[str, Union[str, DataType]]] = None,
+        target: Optional[str] = None,
+        anonymize: Optional[dict] = None,
+        condition_on: Optional[List[str]] = None) -> None:
+    """Fit the synthesizer.
+
+    The synthesizer accepts as training dataset either a pandas [`DataFrame`][pandas.DataFrame] directly or a YData [`DataSource`][ydata.sdk.datasources.DataSource].
+
+    Arguments:
+        X (Union[DataSource, pandas.DataFrame]): Training dataset
+        privacy_level (PrivacyLevel): Synthesizer privacy level (defaults to high fidelity)
+        entities (Union[str, List[str]]): (optional) columns representing entities ID
+        generate_cols (List[str]): (optional) columns that should be synthesized
+        exclude_cols (List[str]): (optional) columns that should not be synthesized
+        dtypes (Dict[str, Union[str, DataType]]): (optional) datatype mapping that will overwrite the datasource metadata column datatypes
+        target (Optional[str]): (optional) Target column
+        name (Optional[str]): (optional) Synthesizer instance name
+        anonymize (Optional[str]): (optional) fields to anonymize and the anonymization strategy
+        condition_on: (Optional[List[str]]): (optional) list of features to condition upon
+    """
+    BaseSynthesizer.fit(self, X=X, datatype=DataSourceType.TABULAR, entities=entities,
+                        generate_cols=generate_cols, exclude_cols=exclude_cols, dtypes=dtypes,
+                        target=target, anonymize=anonymize, privacy_level=privacy_level,
+                        condition_on=condition_on)
+
+
+
+ +
+ + +
+ + + + +

+ sample(n_samples=1, condition_on=None) + +

+ + +
+ +

Sample from a RegularSynthesizer +instance.

+ + + +

Parameters:

+ + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescriptionDefault
n_samples + int + +
+

number of rows in the sample

+
+
+ 1 +
condition_on + Optional[dict] + +
+

(Optional[dict]): (optional) conditional sampling parameters

+
+
+ None +
+ + + +

Returns:

+ + + + + + + + + + + + + +
TypeDescription
+ DataFrame + +
+

synthetic data

+
+
+ +
+ Source code in ydata/sdk/synthesizers/regular.py +
15
+16
+17
+18
+19
+20
+21
+22
+23
+24
+25
+26
+27
+28
+29
+30
+31
+32
+33
+34
def sample(self, n_samples: int = 1, condition_on: Optional[dict] = None) -> pdDataFrame:
+    """Sample from a [`RegularSynthesizer`][ydata.sdk.synthesizers.RegularSynthesizer]
+    instance.
+
+    Arguments:
+        n_samples (int): number of rows in the sample
+        condition_on: (Optional[dict]): (optional) conditional sampling parameters
+
+    Returns:
+        synthetic data
+    """
+    if n_samples < 1:
+        raise InputError("Parameter 'n_samples' must be greater than 0")
+
+    payload = {"numberOfRecords": n_samples}
+    if condition_on is not None:
+        payload["extraData"] = {
+            "condition_on": condition_on
+        }
+    return self._sample(payload=payload)
+
+
+
+ +
+ + + +
+ +
+ +

PrivacyLevel

+ + +
+ + + + +
+

+ Bases: StringEnum

+ + +

Privacy level exposed to the end-user.

+ + + + +
+ + + + + + + +
+ + + + +

+ BALANCED_PRIVACY_FIDELITY = 'BALANCED_PRIVACY_FIDELITY' + + + class-attribute + instance-attribute + + +

+ + +
+ +

Balanced privacy/fidelity

+
+ +
+ +
+ + + + +

+ HIGH_FIDELITY = 'HIGH_FIDELITY' + + + class-attribute + instance-attribute + + +

+ + +
+ +

High fidelity

+
+ +
+ +
+ + + + +

+ HIGH_PRIVACY = 'HIGH_PRIVACY' + + + class-attribute + instance-attribute + + +

+ + +
+ +

High privacy

+
+ +
+ + + + + +
+ +
+ +
+ + + + + + +
+
+ + + + +
+ + + +
+ + + +
+
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/0.7/sdk/reference/api/synthesizers/timeseries/index.html b/0.7/sdk/reference/api/synthesizers/timeseries/index.html new file mode 100644 index 00000000..21802859 --- /dev/null +++ b/0.7/sdk/reference/api/synthesizers/timeseries/index.html @@ -0,0 +1,1991 @@ + + + + + + + + + + + + + + + + + + + + + + TimeSeries - YData Fabric + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ + + + + + +
+ + +
+ +
+ + + + + + + + + +
+
+ + + +
+
+
+ + + + + + + + +
+
+
+ + + + +
+
+ + + + +
+ + + + +
+

+ Bases: BaseSynthesizer

+ + +
+ Source code in ydata/sdk/synthesizers/timeseries.py +
13
+14
+15
+16
+17
+18
+19
+20
+21
+22
+23
+24
+25
+26
+27
+28
+29
+30
+31
+32
+33
+34
+35
+36
+37
+38
+39
+40
+41
+42
+43
+44
+45
+46
+47
+48
+49
+50
+51
+52
+53
+54
+55
+56
+57
+58
+59
+60
+61
+62
+63
+64
+65
+66
+67
+68
+69
+70
+71
+72
+73
+74
class TimeSeriesSynthesizer(BaseSynthesizer):
+
+    def sample(self, n_entities: int, condition_on: Optional[dict] = None) -> pdDataFrame:
+        """Sample from a [`TimeSeriesSynthesizer`][ydata.sdk.synthesizers.TimeSeriesSynthesizer] instance.
+
+        If a training dataset was not using any `entity` column, the Synthesizer assumes a single entity.
+        A [`TimeSeriesSynthesizer`][ydata.sdk.synthesizers.TimeSeriesSynthesizer] always sample the full trajectory of its entities.
+
+        Arguments:
+            n_entities (int): number of entities to sample
+            condition_on: (Optional[dict]): (optional) conditional sampling parameters
+
+        Returns:
+            synthetic data
+        """
+        if n_entities is not None and n_entities < 1:
+            raise InputError("Parameter 'n_entities' must be greater than 0")
+
+        payload = {"numberOfRecords": n_entities}
+        if condition_on is not None:
+            payload["extraData"] = {
+                "condition_on": condition_on
+            }
+        return self._sample(payload=payload)
+
+    def fit(self, X: Union[DataSource, pdDataFrame],
+            sortbykey: Optional[Union[str, List[str]]],
+            privacy_level: PrivacyLevel = PrivacyLevel.HIGH_FIDELITY,
+            entities: Optional[Union[str, List[str]]] = None,
+            generate_cols: Optional[List[str]] = None,
+            exclude_cols: Optional[List[str]] = None,
+            dtypes: Optional[Dict[str, Union[str, DataType]]] = None,
+            target: Optional[str] = None,
+            anonymize: Optional[dict] = None,
+            condition_on: Optional[List[str]] = None) -> None:
+        """Fit the synthesizer.
+
+        The synthesizer accepts as training dataset either a pandas [`DataFrame`][pandas.DataFrame] directly or a YData [`DataSource`][ydata.sdk.datasources.DataSource].
+
+        Arguments:
+            X (Union[DataSource, pandas.DataFrame]): Training dataset
+            sortbykey (Union[str, List[str]]): column(s) to use to sort timeseries datasets
+            privacy_level (PrivacyLevel): Synthesizer privacy level (defaults to high fidelity)
+            entities (Union[str, List[str]]): (optional) columns representing entities ID
+            generate_cols (List[str]): (optional) columns that should be synthesized
+            exclude_cols (List[str]): (optional) columns that should not be synthesized
+            dtypes (Dict[str, Union[str, DataType]]): (optional) datatype mapping that will overwrite the datasource metadata column datatypes
+            target (Optional[str]): (optional) Metadata associated to the datasource
+            name (Optional[str]): (optional) Synthesizer instance name
+            anonymize (Optional[str]): (optional) fields to anonymize and the anonymization strategy
+            condition_on: (Optional[List[str]]): (optional) list of features to condition upon
+        """
+        BaseSynthesizer.fit(self, X=X, datatype=DataSourceType.TIMESERIES, sortbykey=sortbykey,
+                            entities=entities, generate_cols=generate_cols, exclude_cols=exclude_cols,
+                            dtypes=dtypes, target=target, anonymize=anonymize, privacy_level=privacy_level,
+                            condition_on=condition_on)
+
+    def __repr__(self):
+        if self._model is not None:
+            return self._model.__repr__()
+        else:
+            return "TimeSeriesSynthesizer(Not Initialized)"
+
+
+ + + +
+ + + + + + + + + + +
+ + + + +

+ fit(X, sortbykey, privacy_level=PrivacyLevel.HIGH_FIDELITY, entities=None, generate_cols=None, exclude_cols=None, dtypes=None, target=None, anonymize=None, condition_on=None) + +

+ + +
+ +

Fit the synthesizer.

+

The synthesizer accepts as training dataset either a pandas DataFrame directly or a YData DataSource.

+ + + +

Parameters:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescriptionDefault
X + Union[DataSource, DataFrame] + +
+

Training dataset

+
+
+ required +
sortbykey + Union[str, List[str]] + +
+

column(s) to use to sort timeseries datasets

+
+
+ required +
privacy_level + PrivacyLevel + +
+

Synthesizer privacy level (defaults to high fidelity)

+
+
+ HIGH_FIDELITY +
entities + Union[str, List[str]] + +
+

(optional) columns representing entities ID

+
+
+ None +
generate_cols + List[str] + +
+

(optional) columns that should be synthesized

+
+
+ None +
exclude_cols + List[str] + +
+

(optional) columns that should not be synthesized

+
+
+ None +
dtypes + Dict[str, Union[str, DataType]] + +
+

(optional) datatype mapping that will overwrite the datasource metadata column datatypes

+
+
+ None +
target + Optional[str] + +
+

(optional) Metadata associated to the datasource

+
+
+ None +
name + Optional[str] + +
+

(optional) Synthesizer instance name

+
+
+ required +
anonymize + Optional[str] + +
+

(optional) fields to anonymize and the anonymization strategy

+
+
+ None +
condition_on + Optional[List[str]] + +
+

(Optional[List[str]]): (optional) list of features to condition upon

+
+
+ None +
+ +
+ Source code in ydata/sdk/synthesizers/timeseries.py +
38
+39
+40
+41
+42
+43
+44
+45
+46
+47
+48
+49
+50
+51
+52
+53
+54
+55
+56
+57
+58
+59
+60
+61
+62
+63
+64
+65
+66
+67
+68
def fit(self, X: Union[DataSource, pdDataFrame],
+        sortbykey: Optional[Union[str, List[str]]],
+        privacy_level: PrivacyLevel = PrivacyLevel.HIGH_FIDELITY,
+        entities: Optional[Union[str, List[str]]] = None,
+        generate_cols: Optional[List[str]] = None,
+        exclude_cols: Optional[List[str]] = None,
+        dtypes: Optional[Dict[str, Union[str, DataType]]] = None,
+        target: Optional[str] = None,
+        anonymize: Optional[dict] = None,
+        condition_on: Optional[List[str]] = None) -> None:
+    """Fit the synthesizer.
+
+    The synthesizer accepts as training dataset either a pandas [`DataFrame`][pandas.DataFrame] directly or a YData [`DataSource`][ydata.sdk.datasources.DataSource].
+
+    Arguments:
+        X (Union[DataSource, pandas.DataFrame]): Training dataset
+        sortbykey (Union[str, List[str]]): column(s) to use to sort timeseries datasets
+        privacy_level (PrivacyLevel): Synthesizer privacy level (defaults to high fidelity)
+        entities (Union[str, List[str]]): (optional) columns representing entities ID
+        generate_cols (List[str]): (optional) columns that should be synthesized
+        exclude_cols (List[str]): (optional) columns that should not be synthesized
+        dtypes (Dict[str, Union[str, DataType]]): (optional) datatype mapping that will overwrite the datasource metadata column datatypes
+        target (Optional[str]): (optional) Metadata associated to the datasource
+        name (Optional[str]): (optional) Synthesizer instance name
+        anonymize (Optional[str]): (optional) fields to anonymize and the anonymization strategy
+        condition_on: (Optional[List[str]]): (optional) list of features to condition upon
+    """
+    BaseSynthesizer.fit(self, X=X, datatype=DataSourceType.TIMESERIES, sortbykey=sortbykey,
+                        entities=entities, generate_cols=generate_cols, exclude_cols=exclude_cols,
+                        dtypes=dtypes, target=target, anonymize=anonymize, privacy_level=privacy_level,
+                        condition_on=condition_on)
+
+
+
+ +
+ + +
+ + + + +

+ sample(n_entities, condition_on=None) + +

+ + +
+ +

Sample from a TimeSeriesSynthesizer instance.

+

If a training dataset was not using any entity column, the Synthesizer assumes a single entity. +A TimeSeriesSynthesizer always sample the full trajectory of its entities.

+ + + +

Parameters:

+ + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescriptionDefault
n_entities + int + +
+

number of entities to sample

+
+
+ required +
condition_on + Optional[dict] + +
+

(Optional[dict]): (optional) conditional sampling parameters

+
+
+ None +
+ + + +

Returns:

+ + + + + + + + + + + + + +
TypeDescription
+ DataFrame + +
+

synthetic data

+
+
+ +
+ Source code in ydata/sdk/synthesizers/timeseries.py +
15
+16
+17
+18
+19
+20
+21
+22
+23
+24
+25
+26
+27
+28
+29
+30
+31
+32
+33
+34
+35
+36
def sample(self, n_entities: int, condition_on: Optional[dict] = None) -> pdDataFrame:
+    """Sample from a [`TimeSeriesSynthesizer`][ydata.sdk.synthesizers.TimeSeriesSynthesizer] instance.
+
+    If a training dataset was not using any `entity` column, the Synthesizer assumes a single entity.
+    A [`TimeSeriesSynthesizer`][ydata.sdk.synthesizers.TimeSeriesSynthesizer] always sample the full trajectory of its entities.
+
+    Arguments:
+        n_entities (int): number of entities to sample
+        condition_on: (Optional[dict]): (optional) conditional sampling parameters
+
+    Returns:
+        synthetic data
+    """
+    if n_entities is not None and n_entities < 1:
+        raise InputError("Parameter 'n_entities' must be greater than 0")
+
+    payload = {"numberOfRecords": n_entities}
+    if condition_on is not None:
+        payload["extraData"] = {
+            "condition_on": condition_on
+        }
+    return self._sample(payload=payload)
+
+
+
+ +
+ + + +
+ +
+ +

PrivacyLevel

+ + +
+ + + + +
+

+ Bases: StringEnum

+ + +

Privacy level exposed to the end-user.

+ + + + +
+ + + + + + + +
+ + + + +

+ BALANCED_PRIVACY_FIDELITY = 'BALANCED_PRIVACY_FIDELITY' + + + class-attribute + instance-attribute + + +

+ + +
+ +

Balanced privacy/fidelity

+
+ +
+ +
+ + + + +

+ HIGH_FIDELITY = 'HIGH_FIDELITY' + + + class-attribute + instance-attribute + + +

+ + +
+ +

High fidelity

+
+ +
+ +
+ + + + +

+ HIGH_PRIVACY = 'HIGH_PRIVACY' + + + class-attribute + instance-attribute + + +

+ + +
+ +

High privacy

+
+ +
+ + + + + +
+ +
+ +
+ + + + + + +
+
+ + + + +
+ + + +
+ + + +
+
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/0.7/sdk/reference/changelog/index.html b/0.7/sdk/reference/changelog/index.html new file mode 100644 index 00000000..6a69618e --- /dev/null +++ b/0.7/sdk/reference/changelog/index.html @@ -0,0 +1,1248 @@ + + + + + + + + + + + + + + + + + + + + + + Changelog - YData Fabric + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+
+ +
+ + + + + + +
+ + +
+ +
+ + + + + + + + + +
+
+ + + +
+
+
+ + + + + + + + +
+
+
+ + + + +
+
+ + + + +

Changelog

+ + + + + + + + +
+
+ + + + +
+ + + +
+ + + +
+
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/0.7/search/search_index.json b/0.7/search/search_index.json new file mode 100644 index 00000000..d3fbf2be --- /dev/null +++ b/0.7/search/search_index.json @@ -0,0 +1 @@ +{"config":{"lang":["en"],"separator":"[\\s\\-]+","pipeline":["stopWordFilter"]},"docs":[{"location":"","title":"Welcome","text":"

YData Fabric is a Data-Centric AI development platform that accelerates AI development by helping data practitioners achieve production-quality data.

Much like for software engineering the quality of code is a must for the success of software development, Fabric accounts for the data quality requirements for data-driven applications. It introduces standards, processes, and acceleration to empower data science, analytics, and data engineering teams.

"},{"location":"#try-fabric","title":"Try Fabric","text":"
  • Get started with Fabric Community
"},{"location":"#why-adopt-ydata-fabric","title":"Why adopt YData Fabric?","text":"

With Fabric, you can standardize the understanding of your data, quickly identify data quality issues, streamline and version your data preparation workflows and finally leverage synthetic data for privacy-compliance or as a tool to boost ML performance. Fabric is a development environment that supports a faster and easier process of preparing data for AI development. Data practitioners are using Fabric to:

  • Establish a centralized and collaborative repository for data projects.
  • Create and share comprehensive documentation of data, encompassing data schema, structure, and personally identifiable information (PII).
  • Prevent data quality issues with standardized data quality profiling, providing visual understanding and warnings on potential issues.
  • Accelerate data preparation with customizable recipes.
  • Improve machine learning performance with optimal data preparation through solutions such as synthetic data.
  • Shorten access to data with privacy-compliant synthetic data generatio.
  • Build and streamline data preparation workflows effortlessly through a user-friendly drag-and-drop interface.
  • Efficiently manage business rules, conduct comparisons, and implement version control for data workflows using pipelines.
"},{"location":"#key-features","title":"\ud83d\udcdd Key features","text":""},{"location":"#data-catalog","title":"Data Catalog","text":"

Fabric Data Catalog provides a centralized perspective on datasets within a project-basis, optimizing data management through seamless integration with the organization's existing data architectures via scalable connectors (e.g., MySQL, Google Cloud Storage, AWS S3). It standardizes data quality profiling, streamlining the processes of efficient data cleaning and preparation, while also automating the identification of Personally Identifiable Information (PII) to facilitate compliance with privacy regulations.

Explore how a Data Catalog through a centralized repository of your datasets, schema validation, and automated data profiling.

"},{"location":"#labs","title":"Labs","text":"

Fabric's Labs environments provide collaborative, scalable, and secure workspaces layered on a flexible infrastructure, enabling users to seamlessly switch between CPUs and GPUs based on their computational needs. Labs are familiar environments that empower data developers with powerful IDEs (Jupyter Notebooks, Visual Code or H2O flow) and a seamless experience with the tools they already love combined with YData's cutting-edge SDK for data preparation.

Learn how to use the Labs to generate synthetic data in a familiar Python interface.

"},{"location":"#synthetic-data","title":"Synthetic data","text":"

Synthetic data, enabled by YData Fabric, provides data developers with a user-friendly interfaces (UI and code) for generating artificial datasets, offering a versatile solution across formats like tabular, time-series and multi-table datasets. The generated synthetic data holds the same value of the original and aligns intricately with specific business rules, contributing to machine learning models enhancement, mitigation of privacy concerns and more robustness for data developments. Fabric offers synthetic data that is ease to adapt and configure, allows customization in what concerns privacy-utility trade-offs.

Learn how you to create high-quality synthetic data within a user-friendly UI using Fabric\u2019s data synthesis flow.

"},{"location":"#pipelines","title":"Pipelines","text":"

Fabric Pipelines streamlines data preparation workflows by automating, orchestrating, and optimizing data pipelines, providing benefits such as flexibility, scalability, monitoring, and reproducibility for efficient and reliable data processing. The intuitive drag-and-drop interface, leveraging Jupyter notebooks or Python scripts, expedites the pipeline setup process, providing data developers with a quick and user-friendly experience.

Explore how you can leverage Fabric Pipelines to build versionable and reproducible data preparation workflows for ML development.

"},{"location":"#tutorials","title":"Tutorials","text":"

To understand how to best apply Fabric to your use cases, start by exploring the following tutorials:

  • Handling Imbalanced Data for Improved Fraud DetectionLearn how to implement high-performant fraud detection models by incorporating synthetic data to balance your datasets.

  • Prediction with Quality Inspection Learn how to develop data preparation workflows with automated data quality checks and Pipelines.

  • Generating Synthetic Data for Financial TransactionsLearn how to use synthetic data generation to replicate your existing relational databases while ensuring referential integrity.

You can find additional examples and use cases at YData Academy GitHub Repository.

"},{"location":"#support","title":"\ud83d\ude4b Support","text":"

Facing an issue? We\u2019re committed to providing all the support you need to ensure a smooth experience using Fabric:

  • Create a support ticket: our team will help you move forward!
  • Contact a Fabric specialist: for personalized guidance or full access to the platform
"},{"location":"examples/synthesize_tabular_data/","title":"Synthesize tabular data","text":"

Use YData's RegularSynthesizer to generate tabular synthetic data

import os\nfrom ydata.sdk.dataset import get_dataset\nfrom ydata.sdk.synthesizers import RegularSynthesizer\n# Do not forget to add your token as env variables\nos.environ[\"YDATA_TOKEN\"] = '<TOKEN>'  # Remove if already defined\ndef main():\n\"\"\"In this example, we demonstrate how to train a synthesizer from a pandas\n    DataFrame.\n    After training a Regular Synthesizer, we request a sample.\n    \"\"\"\nX = get_dataset('census')\n# We initialize a regular synthesizer\n# As long as the synthesizer does not call `fit`, it exists only locally\nsynth = RegularSynthesizer()\n# We train the synthesizer on our dataset\nsynth.fit(X)\n# We request a synthetic dataset with 50 rows\nsample = synth.sample(n_samples=50)\nprint(sample.shape)\nif __name__ == \"__main__\":\nmain()\n
"},{"location":"examples/synthesize_timeseries_data/","title":"Synthesize time-series data","text":"

Use YData's TimeSeriesSynthesizer to generate time-series synthetic data

Tabular data is the most common type of data we encounter in data problems.

When thinking about tabular data, we assume independence between different records, but this does not happen in reality. Suppose we check events from our day-to-day life, such as room temperature changes, bank account transactions, stock price fluctuations, and air quality measurements in our neighborhood. In that case, we might end up with datasets where measures and records evolve and are related through time. This type of data is known to be sequential or time-series data.

Thus, sequential or time-series data refers to any data containing elements ordered into sequences in a structured format. Dissecting any time-series dataset, we see differences in variables' behavior that need to be understood for an effective generation of synthetic data. Typically any time-series dataset is composed of the following:

  • Variables that define the order of time (these can be simple with one variable or composed)
  • Time-variant variables
  • Variables that refer to entities (single or multiple entities)
  • Variables that are attributes (those that don't depend on time but rather on the entity)

Below find an example:

import os\nfrom ydata.sdk.dataset import get_dataset\nfrom ydata.sdk.synthesizers import TimeSeriesSynthesizer\n# Do not forget to add your token as env variable\nos.environ[\"YDATA_TOKEN\"] = '<TOKEN>'\nX = get_dataset('occupancy')\n# We initialize a time series synthesizer\n# As long as the synthesizer does not call `fit`, it exists only locally\nsynth = TimeSeriesSynthesizer()\n# We train the synthesizer on our dataset\n# sortbykey -> variable that define the time order for the sequence\nsynth.fit(X, sortbykey='date')\n# By default it is requested a synthetic sample with the same length as the original data\n# The TimeSeriesSynthesizer is designed to replicate temporal series and therefore the original time-horizon is respected\nsample = synth.sample(n_entities=1)\n
"},{"location":"examples/synthesize_with_anonymization/","title":"Anonymization","text":"

YData Synthesizers offers a way to anonymize sensitive information such that the original values are not present in the synthetic data but replaced by fake values.

Does the model retain the original values?

No! The anonymization is performed before the model training such that it never sees the original values.

The anonymization is performed by specifying which columns need to be anonymized and how to perform the anonymization. The anonymization rules are defined as a dictionary with the following format:

{column_name: anonymization_rule}

While here are some predefined anonymization rules such as name, email, company, it is also possible to create a rule using a regular expression. The anonymization rules have to be passed to a synthesizer in its fit method using the parameter anonymize.

What is the difference between anonymization and privacy?

Anonymization makes sure sensitive information are hidden from the data. Privacy makes sure it is not possible to infer the original data points from the synthetic data points via statistical attacks.

Therefore, for data sharing anonymization and privacy controls are complementary.

The example below demonstrates how to anonymize the column Name by fake names and the column Ticket by a regular expression:

import os\nfrom ydata.sdk.dataset import get_dataset\nfrom ydata.sdk.synthesizers import RegularSynthesizer\n# Do not forget to add your token as env variables\nos.environ[\"YDATA_TOKEN\"] = '<TOKEN>'  # Remove if already defined\ndef main():\n\"\"\"In this example, we demonstrate how to train a synthesizer from a pandas\n    DataFrame.\n    After training a Regular Synthesizer, we request a sample.\n    \"\"\"\nX = get_dataset('titanic')\n# We initialize a regular synthesizer\n# As long as the synthesizer does not call `fit`, it exists only locally\nsynth = RegularSynthesizer()\n# We define anonymization rules, which is a dictionary with format:\n# {column_name: anonymization_rule, ...}\n# while here are some predefined anonymization rules like: name, email, company\n# it is also possible to create a rule using a regular expression\nrules = {\n\"Name\": \"name\",\n\"Ticket\": \"[A-Z]{2}-[A-Z]{4}\"\n}\n# We train the synthesizer on our dataset\nsynth.fit(\nX,\nname=\"titanic_synthesizer\",\nanonymize=rules\n)\n# We request a synthetic dataset with 50 rows\nsample = synth.sample(n_samples=50)\nprint(sample[[\"Name\", \"Ticket\"]].head(3))\nif __name__ == \"__main__\":\nmain()\n

"},{"location":"examples/synthesize_with_conditional_sampling/","title":"Conditional sampling","text":"

YData Synthesizers support conditional sampling. The fit method has an optional parameter named condition_on, which receives a list of features to condition upon. Furthermore, the sample method receives the conditions to be applied through another optional parameter also named condition_on. For now, two types of conditions are supported:

  • Condition upon a categorical (or string) feature. The parameters are the name of the feature and a list of values (i.e., categories) to be considered. Each category also has its percentage of representativeness. For example, if we want to condition upon two categories, we need to define the percentage of rows each of these categories will have on the synthetic dataset. Naturally, the sum of such percentages needs to be 1. The default percentage is also 1 since it is the required value for a single category.
  • Condition upon a numerical feature. The parameters are the name of the feature and the minimum and maximum of the range to be considered. This feature will present a uniform distribution on the synthetic dataset, limited by the specified range.

The example below demonstrates how to train and sample from a synthesizer using conditional sampling:

import os\nfrom ydata.sdk.dataset import get_dataset\nfrom ydata.sdk.synthesizers import RegularSynthesizer\n# Do not forget to add your token as env variables.\nos.environ[\"YDATA_TOKEN\"] = '<TOKEN>'  # Remove if already defined.\ndef main():\n\"\"\"In this example, we demonstrate how to train and\n    sample from a synthesizer using conditional sampling.\"\"\"\nX = get_dataset('census')\n# We initialize a regular synthesizer.\n# As long as the synthesizer does not call `fit`, it exists only locally.\nsynth = RegularSynthesizer()\n# We train the synthesizer on our dataset setting\n# the features to condition upon.\nsynth.fit(\nX,\nname=\"census_synthesizer\",\ncondition_on=[\"sex\", \"native-country\", \"age\"]\n)\n# We request a synthetic dataset with specific condition rules.\nsample = synth.sample(\nn_samples=500,\ncondition_on={\n\"sex\": {\n\"categories\": [\"Female\"]\n},\n\"native-country\": {\n\"categories\": [(\"United-States\", 0.6),\n(\"Mexico\", 0.4)]\n},\n\"age\": {\n\"minimum\": 55,\n\"maximum\": 60\n}\n}\n)\nprint(sample)\nif __name__ == \"__main__\":\nmain()\n
"},{"location":"examples/synthesize_with_privacy_control/","title":"Privacy control","text":"

YData Synthesizers offers 3 different levels of privacy:

  1. high privacy: the model is optimized for privacy purposes,
  2. high fidelity (default): the model is optimized for high fidelity,
  3. balanced: tradeoff between privacy and fidelity.

The default privacy level is high fidelity. The privacy level can be changed by the user at the moment a synthesizer level is trained by using the parameter privacy_level. The parameter expect a PrivacyLevel value.

What is the difference between anonymization and privacy?

Anonymization makes sure sensitive information are hidden from the data. Privacy makes sure it is not possible to infer the original data points from the synthetic data points via statistical attacks.

Therefore, for data sharing anonymization and privacy controls are complementary.

The example below demonstrates how to train a synthesizer configured for high privacy:

import os\nfrom ydata.sdk.dataset import get_dataset\nfrom ydata.sdk.synthesizers import PrivacyLevel, RegularSynthesizer\n# Do not forget to add your token as env variables\nos.environ[\"YDATA_TOKEN\"] = '<TOKEN>'  # Remove if already defined\ndef main():\n\"\"\"In this example, we demonstrate how to train a synthesizer\n    with a high-privacy setting from a pandas DataFrame.\n    After training a Regular Synthesizer, we request a sample.\n    \"\"\"\nX = get_dataset('titanic')\n# We initialize a regular synthesizer\n# As long as the synthesizer does not call `fit`, it exists only locally\nsynth = RegularSynthesizer()\n# We train the synthesizer on our dataset setting the privacy level to high\nsynth.fit(\nX,\nname=\"titanic_synthesizer\",\nprivacy_level=PrivacyLevel.HIGH_PRIVACY\n)\n# We request a synthetic dataset with 50 rows\nsample = synth.sample(n_samples=50)\nprint(sample)\nif __name__ == \"__main__\":\nmain()\n
"},{"location":"get-started/","title":"Get started with Fabric","text":"

The get started is here to help you if you are not yet familiar with YData Fabric or if you just want to learn more about data quality, data preparation workflows and how you can start leveraging synthetic data. Mention to YData Fabric Community

"},{"location":"get-started/#create-your-first-data-with-the-data-catalog","title":"\ud83d\udcda Create your first Data with the Data Catalog","text":""},{"location":"get-started/#create-your-first-synthetic-data-generator","title":"\u2699\ufe0f Create your first Synthetic Data generator","text":""},{"location":"get-started/#create-your-first-lab","title":"\ud83e\uddea Create your first Lab","text":""},{"location":"get-started/#create-your-first-data-pipeline","title":"\ud83c\udf00 Create your first data Pipeline","text":""},{"location":"get-started/create_lab/","title":"How to create your first Lab environment","text":"

Labs are code environments for a more flexible development of data-driven solutions while leveraging Fabric capabilities combined with already loved tools such as scikit-learn, numpy and pandas. To create your first Lab, you can use the \u201cCreate Lab\u201d from Fabric\u2019s home, or you can access it from the Labs module by selecting it on the left side menu, and clicking the \u201cCreate Lab\u201d button.

Next, a menu with different IDEs will be shown. As a quickstart select Jupyter Lab. As labs are development environments you will be also asked what language you would prefer your environment to support: R or Python. Select Python.

Select IDE Select language

Bundles are environments with pre-installed packages. Select YData bundle, so we can leverage some other Fabric features such as Data Profiling, Synthetic Data and Pipelines.

As a last step, you will be asked to configure the infrastructure resources for this new environment as well as giving it a Display Name. We will keep the defaults, but you have flexibility to select GPU acceleration or whether you need more computational resources for your developments.

Finally, your Lab will be created and added to the \"Labs\" list, as per the image below. The status of the lab will be \ud83d\udfe1 while preparing, and this process takes a few minutes, as the infrastructure is being allocated to your development environment. As soon as the status changes to \ud83d\udfe2, you can open your lab by clicking in the button as shown below:

Create a new notebook in the JupyterLab and give it a name. You are now ready to start your developments!

Create a new notebook Notebook created

Congrats! \ud83d\ude80 You have now successfully created your first Lab a code environment, so you can benefit from the most advanced Fabric features as well as compose complex data workflows. Get ready for your journey of improved quality data for AI.

"},{"location":"get-started/create_pipeline/","title":"How to create your first Pipeline","text":"

Check this quickstart video on how to create your first Pipeline.

The best way to get started with Pipelines is to use the interactive Pipeline editor available in the Labs with Jupyter Lab set as IDE. If you don't have a Lab yet, or you don't know how to create one, check our quickstart guide on how to create your first lab.

Open an already existing lab.

A Pipeline comprises one or more nodes that are connected (or not!) with each other to define execution dependencies. Each pipeline node is and should be implemented as a component that is expected to manage a single task, such as read the data, profiling the data, training a model, or even publishing a model to production environments.

In this tutorial we will build a simple and generic pipeline that use a Dataset from Fabric's Data Catalog and profile to check it's quality. We have the notebooks template already available. For that you need to access the \"Academy\" folder as per the image below.

Make sure to copy all the files in the folder \"3 - Pipelines/quickstart\" to the root folder of your lab, as per the image below.

Now that we have our notebooks we need to make a small change in the notebook \"1. Read dataset\". Go back to your Data Catalog, from one of the datasets in your Catalog list, select the three vertical dots and click in \"Explore in Labs\" as shown in the image below.

The following screen will be shown. Click in copy.

Now that we have copied the code, let's get back to our \"1. Read data.ipynb\" notebook, and replace the first code cell by with the new code. This will allow us to use a dataset from the Data Catalog in our pipeline.

Placeholder code Replaced with code snippet

With our notebooks ready, we can now configure our Pipeline. For this quickstart we will be leveraging an already existing pipeline - double-click the file my_first_pipeline.pipeline. You should see a pipeline as depicted in the images below. To create a new Pipeline, you can open the lab launcher tab and select \"Pipeline Editor\".

Open Pipeline My first pipeline

Before running the pipeline, we need to check each component/step properties and configurations. Right-click each one of the steps, select \"Open Properties\", and a menu will be depicted in your right side. Make sure that you have \"YData - CPU\" selected as the Runtime Image as show below.

Open properties Runtime image

We are now ready to create and run our first pipeline. In the top left corner of the pipeline editor, the run button will be available for you to click.

Accept the default values shown in the run dialog and start the run

If the following message is shown, it means that you have create a run of your first pipeline.

Now that you have created your first pipeline, you can select the Pipeline from Fabric's left side menu.

Your most recent pipeline will be listed, as shown in below image.

To check the run of your pipeline, jump into the \"Run\" tab. You will be able to see your first pipeline running!

By clicking on top of the record you will be able to see the progress of the run step-by-step, and visualize the outputs of each and every step by clicking on each step and selecting the Visualizations tab.

Congrats! \ud83d\ude80 You have now successfully created your first Pipeline a code environment, so you can benefit from Fabric's orchestration engine to crate scalable, versionable and comparable data workflows. Get ready for your journey of improved quality data for AI.

"},{"location":"get-started/create_syntheticdata_generator/","title":"How to create your first Synthetic Data generator","text":"

Check this quickstart video on how to create your first Synthetic Data generator.

To generate your first synthetic data, you need to have a Dataset already available in your Data Catalog. Check this tutorial to see how you can add your first dataset to Fabric\u2019s Data Catalog.

With your first dataset created, you are now able to start the creation of your Synthetic Data generator. You can either select \"Synthetic Data\" from your left side menu, or you can select \"Create Synthetic Data\" in your project Home as shown in the image below.

You'll be asked to select the dataset you wish to generate synthetic data from and verify the columns you'd like to include in the synthesis process, validating their Variable and Data Types.

Data types are relevant for synthetic data quality

Data Types are important to be revisited and aligned with the objectives for the synthetic data as they can highly impact the quality of the generated data. For example, let's say we have a column that is a \"Name\", while is some situations it would make sense to consider it a String, under the light of a dataset where \"Name\" refers to the name of the product purchases, it might be more beneficial to set it as a Category.

Finally, as the last step of our process it comes the Synthetic Data specific configurations, for this particular case we only need to define a Display Name, and we can finish the process by clicking in the \"Save\" button as per the image below.

Your Synthetic Data generator is now training and listed under \"Synthetic Data\". While the model is being trained, the Status will be \ud83d\udfe1, as soon as the training is completed successfully it will transition to \ud83d\udfe2 as per the image below.

Once the Synthetic Data generator has finished training, you're ready to start generating your first synthetic dataset. You can start by exploring an overview of the model configurations and even download a PDF report with a comprehensive overview of your Synthetic Data Quality Metrics. Next, you can generate synthetic data samples by accessing the Generation tab or click on \"Go to Generation\".

In this section, you are able to generate as many synthetic samples as you want. For that you need to define the number rows to generate and click \"Generate\", as depicted in the image below.

A new line in your \"Sample History\" will be shown and as soon as the sample generation is completed you will be able to \"Compare\" your synthetic data with the original data, add as a Dataset with \"Add to Data Catalog\" and last but not the least download it as a file with \"Download csv\".

Congrats! \ud83d\ude80 You have now successfully created your first Synthetic Data generator with Fabric. Get ready for your journey of improved quality data for AI.

"},{"location":"get-started/fabric_community/","title":"Get started with Fabric Community","text":"

Fabric Community is a SaaS version that allows you to explore all the functionalities of Fabric first-hand: free, forever, for everyone. You\u2019ll be able to validate your data quality with automated profiling, unlock data sharing and improve your ML models with synthetic data, and increase your productivity with seamless integration:

  • Build 1 personal project;
  • Create your first Data Catalog and benefit from automated data profiling;
  • Train and generate synthetic data up to 2 models and datasets with 50 columns and 100K rows;
  • Optimize synthetic data quality for your use cases with an evaluation PDF report;
  • Create 1 development environment (Labs) and integrate it with your familiar ML packages and workflows.
"},{"location":"get-started/fabric_community/#register","title":"Register","text":"

To register for Fabric Community:

  • Access the Fabric Community Try Now and create your YData account by submitting the form
  • Check your email for your login credentials
  • Login into fabric.ydata.ai and enjoy!

Once you login, you'll access the Home page and get started with your data preparation!

"},{"location":"get-started/upload_csv/","title":"How to create your first Dataset from a CSV file","text":"

Check this quickstart video on how to create your first Dataset from a CSV file.

To create your first dataset in the Data Catalog, you can start by clicking on \"Add Dataset\" from the Home section. Or click to Data Catalog (on the left side menu) and click \u201cAdd Dataset\u201d.

After that the below modal will be shown. You will need to select a connector. To upload a CSV file, we need to select \u201cUpload CSV\u201d.

Once you've selected the \u201cUpload CSV\u201d connector, a new screen will appear, enabling you to upload your file and designate a name for your connector. This file upload connector will subsequently empower you to create one or more datasets from the same file at a later stage.

Loading area Upload csv file

With the Connector created, you'll be able to add a dataset and specify its properties:

  • Name: The name of your dataset;
  • Separator: This is an important parameter to make sure that we can parse your CSV correctly. The default value is \u201c,\u201d.
  • Data Type: Whether your dataset contains tabular or time-series (i.e., containing temporal dependency) data.

Your created Connector (\u201cCensus File\u201d) and Dataset (\u201cCensus\u201d) will be added to the Data Catalog. As soon as the status is green, you can navigate your Dataset. Click in Open Dataset as per the image below.

Within the Dataset details, you can gain valuable insights through our automated data quality profiling. This includes comprehensive metadata and an overview of your data, encompassing details like row count, identification of duplicates, and insights into the overall quality of your dataset.

Or perhaps, you want to further explore through visualization, the profile of your data with both univariate and multivariate of your data.

Congrats! \ud83d\ude80 You have now successfully created your first Connector and Dataset in Fabric\u2019s Data Catalog. Get ready for your journey of improved quality data for AI.

"},{"location":"sdk/","title":"Overview","text":"

YData SDK for improved data quality everywhere!

ydata-sdk is here! Create a YData account so you can start using today!

Create account

"},{"location":"sdk/#overview","title":"Overview","text":"

The YData SDK is an ecosystem of methods that allows users to, through a python interface, adopt a Data-Centric approach towards the AI development. The solution includes a set of integrated components for data ingestion, standardized data quality evaluation and data improvement, such as synthetic data generation, allowing an iterative improvement of the datasets used in high-impact business applications.

Synthetic data can be used as Machine Learning performance enhancer, to augment or mitigate the presence of bias in real data. Furthermore, it can be used as a Privacy Enhancing Technology, to enable data-sharing initiatives or even to fuel testing environments.

Under the YData-SDK hood, you can find a set of algorithms and metrics based on statistics and deep learning based techniques, that will help you to accelerate your data preparation.

"},{"location":"sdk/#current-functionality","title":"Current functionality","text":"

YData SDK is currently composed by the following main modules:

  • Datasources

    • YData\u2019s SDK includes several connectors for easy integration with existing data sources. It supports several storage types, like filesystems and RDBMS. Check the list of connectors.
    • SDK\u2019s Datasources run on top of Dask, which allows it to deal with not only small workloads but also larger volumes of data.
  • Synthesizers

    • Simplified interface to train a generative model and learn in a data-driven manner the behavior, the patterns and original data distribution. Optimize your model for privacy or utility use-cases.
    • From a trained synthesizer, you can generate synthetic samples as needed and parametrise the number of records needed.
    • Anonymization and privacy preserving capabilities to ensure that synthetic datasets does not contain Personal Identifiable Information (PII) and can safely be shared!
    • Conditional sampling can be used to restrict the domain and values of specific features in the sampled data.
  • Synthetic data quality report Coming soon

    • An extensive synthetic data quality report that measures 3 dimensions: privacy, utility and fidelity of the generated data. The report can be downloaded in PDF format for ease of sharing and compliance purposes or as a JSON to enable the integration in data flows.
  • Profiling Coming soon

    • A set of metrics and algorithms summarizes datasets quality in three main dimensions: warnings, univariate analysis and a multivariate perspective.
"},{"location":"sdk/#supported-data-formats","title":"Supported data formats","text":"TabularTime-SeriesTransactionalRelational databases

The RegularSynthesizer is perfect to synthesize high-dimensional data, that is time-indepentent with high quality results.

Know more

The TimeSeriesSynthesizer is perfect to synthesize both regularly and not evenly spaced time-series, from smart-sensors to stock.

Know more

The TimeSeriesSynthesizer supports transactional data, known to have highly irregular time intervals between records and directional relations between entities.

Coming soon

Know more

The MultiTableSynthesizer is perfect to learn how to replicate the data within a relational database schema.

Coming soon

Know more

"},{"location":"sdk/installation/","title":"Installation","text":"

YData SDK is generally available through both Pypi and Conda allowing an easy process of installation. This experience allows combining YData SDK with other packages such as Pandas, Numpy or Scikit-Learn.

YData SDK is available for the public through a token-based authentication system. If you don\u2019t have one yet, you can get your free license key during the installation process. You can check what features are available in the free version here.

"},{"location":"sdk/installation/#installing-the-package","title":"Installing the package","text":"

YData SDK supports python versions bigger than python 3.8, and can be installed in Windows, Linux or MacOS operating systems.

Prior to the package installation, it is recommended the creation of a virtual or conda environment:

pyenv
pyenv virtualenv 3.10 ydatasdk\n

And install ydata-sdk

pypi
pip install ydata-sdk\n
"},{"location":"sdk/installation/#authentication","title":"Authentication","text":"

Once you've installed ydata-sdk package you will need a token to run the functionalities. YData SDK uses a token based authentication system. To get access to your token, you need to create a YData account.

YData SDK offers a free-trial and an enterprise version. To access your free-trial token, you need to create a YData account.

The token will be available here, after login:

With your account toke copied, you can set a new environment variable YDATA_TOKEN in the beginning of your development session.

    import os\nos.setenv['YDATA_TOKEN'] = '{add-your-token}'\n

Once you have set your token, you are good to go to start exploring the incredible world of data-centric AI and smart synthetic data generation!

Check out our quickstart guide!

"},{"location":"sdk/quickstart/","title":"Quickstart","text":"

YData SDK allows you to with an easy and familiar interface, to adopt a Data-Centric AI approach for the development of Machine Learning solutions. YData SDK features were designed to support structure data, including tabular data, time-series and transactional data.

"},{"location":"sdk/quickstart/#read-data","title":"Read data","text":"

To start leveraging the package features you should consume your data either through the Connectors or pandas.Dataframe. The list of available connectors can be found here [add a link].

From pandas dataframeFrom a connector
    # Example for a Google Cloud Storage Connector\ncredentials = \"{insert-credentials-file-path}\"\n# We create a new connector for Google Cloud Storage\nconnector = Connector(connector_type='gcs', credentials=credentials)\n# Create a Datasource from the connector\n# Note that a connector can be re-used for several datasources\nX = DataSource(connector=connector, path='gs://<my_bucket>.csv')\n
    # Load a small dataset\nX = pd.read_csv('{insert-file-path.csv}')\n# Init a synthesizer\nsynth = RegularSynthesizer()\n# Train the synthesizer with the pandas Dataframe as input\n# The data is then sent to the cluster for processing\nsynth.fit(X)\n

The synthesis process returns a pandas.DataFrame object. Note that if you are using the ydata-sdk free version, all of your data is sent to a remote cluster on YData's infrastructure.

"},{"location":"sdk/quickstart/#data-synthesis-flow","title":"Data synthesis flow","text":"

The process of data synthesis can be described into the following steps:

stateDiagram-v2\n  state read_data\n  read_data --> init_synth\n  init_synth --> train_synth\n  train_synth --> generate_samples\n  generate_samples --> [*]

The code snippet below shows how easy can be to start generating new synthetic data. The package includes a set of examples datasets for a quickstart.

    from ydata.sdk.dataset import get_dataset\n#read the example data\nX = get_dataset('census')\n# Init a synthesizer\nsynth = RegularSynthesizer()\n# Fit the synthesizer to the input data\nsynth.fit(X)\n# Sample new synthetic data. The below request ask for new 1000 synthetic rows\nsynth.sample(n_samples=1000)\n

Do I need to prepare my data before synthesis?

The sdk ensures that the original behaviour is replicated. For that reason, there is no need to preprocess outlier observations or missing data.

By default all the missing data is replicated as NaN.

"},{"location":"sdk/modules/connectors/","title":"Connectors","text":"

YData SDK allows users to consume data assets from remote storages through Connectors. YData Connectors support different types of storages, from filesystems to RDBMS'.

Below the list of available connectors:

Connector Name Type Supported File Types Useful Links Notes AWS S3 Remote object storage CSV, Parquet https://aws.amazon.com/s3/ Google Cloud Storage Remote object storage CSV, Parquet https://cloud.google.com/storage Azure Blob Storage Remote object storage CSV, Parquet https://azure.microsoft.com/en-us/services/storage/blobs/ File Upload Local CSV - Maximum file size is 220MB. Bigger files should be uploaded and read from remote object storages MySQL RDBMS Not applicable https://www.mysql.com/ Supports reading whole schemas or specifying a query Azure SQL Server RDBMS Not applicable https://azure.microsoft.com/en-us/services/sql-database/campaign/ Supports reading whole schemas or specifying a query PostgreSQL RDBMS Not applicable https://www.postgresql.org/ Supports reading whole schemas or specifying a query Snowflake RDBMS Not applicable https://docs.snowflake.com/en/sql-reference-commands Supports reading whole schemas or specifying a query Google BigQuery Data warehouse Not applicable https://cloud.google.com/bigquery Azure Data Lake Data lake CSV, Parquet https://azure.microsoft.com/en-us/services/storage/data-lake-storage/

More details can be found at Connectors APi Reference Docs.

"},{"location":"sdk/modules/synthetic_data/","title":"Synthetic data generation","text":""},{"location":"sdk/modules/synthetic_data/#data-formats","title":"Data formats","text":""},{"location":"sdk/modules/synthetic_data/#tabular-data","title":"Tabular data","text":""},{"location":"sdk/modules/synthetic_data/#time-series-data","title":"Time-series data","text":""},{"location":"sdk/modules/synthetic_data/#transactions-data","title":"Transactions data","text":""},{"location":"sdk/modules/synthetic_data/#best-practices","title":"Best practices","text":""},{"location":"sdk/reference/api/common/client/","title":"Get client","text":"

Deduce how to initialize or retrieve the client.

This is meant to be a zero configuration for the user.

Create and set a client globally
from ydata.sdk.client import get_client\nget_client(set_as_global=True)\n

Parameters:

Name Type Description Default client_or_creds Optional[Union[Client, dict, str, Path]]

Client to forward or credentials for initialization

None set_as_global bool

If True, set client as global

False wait_for_auth bool

If True, wait for the user to authenticate

True

Returns:

Type Description Client

Client instance

Source code in ydata/sdk/common/client/utils.py
def get_client(client_or_creds: Optional[Union[Client, Dict, str, Path]] = None, set_as_global: bool = False, wait_for_auth: bool = True) -> Client:\n\"\"\"Deduce how to initialize or retrieve the client.\n    This is meant to be a zero configuration for the user.\n    Example: Create and set a client globally\n            ```py\n            from ydata.sdk.client import get_client\n            get_client(set_as_global=True)\n            ```\n    Args:\n        client_or_creds (Optional[Union[Client, dict, str, Path]]): Client to forward or credentials for initialization\n        set_as_global (bool): If `True`, set client as global\n        wait_for_auth (bool): If `True`, wait for the user to authenticate\n    Returns:\n        Client instance\n    \"\"\"\nclient = None\nglobal WAITING_FOR_CLIENT\ntry:\n# If a client instance is set globally, return it\nif not set_as_global and Client.GLOBAL_CLIENT is not None:\nreturn Client.GLOBAL_CLIENT\n# Client exists, forward it\nif isinstance(client_or_creds, Client):\nreturn client_or_creds\n# Explicit credentials\n''' # For the first version, we deactivate explicit credentials via string or file for env var only\n        if isinstance(client_or_creds, (dict, str, Path)):\n            if isinstance(client_or_creds, str):  # noqa: SIM102\n                if Path(client_or_creds).is_file():\n                    client_or_creds = Path(client_or_creds)\n            if isinstance(client_or_creds, Path):\n                client_or_creds = json.loads(client_or_creds.open().read())\n            return Client(credentials=client_or_creds)\n        # Last try with environment variables\n        #if client_or_creds is None:\n        client = _client_from_env(wait_for_auth=wait_for_auth)\n        '''\ncredentials = environ.get(TOKEN_VAR)\nif credentials is not None:\nclient = Client(credentials=credentials)\nexcept ClientHandshakeError as e:\nwait_for_auth = False  # For now deactivate wait_for_auth until the backend is ready\nif wait_for_auth:\nWAITING_FOR_CLIENT = True\nstart = time()\nlogin_message_printed = False\nwhile client is None:\nif not login_message_printed:\nprint(\nf\"The token needs to be refreshed - please validate your token by browsing at the following URL:\\n\\n\\t{e.auth_link}\")\nlogin_message_printed = True\nwith suppress(ClientCreationError):\nsleep(BACKOFF)\nclient = get_client(wait_for_auth=False)\nnow = time()\nif now - start > CLIENT_INIT_TIMEOUT:\nWAITING_FOR_CLIENT = False\nbreak\nif client is None and not WAITING_FOR_CLIENT:\nsys.tracebacklimit = None\nraise ClientCreationError\nreturn client\n

Main Client class used to abstract the connection to the backend.

A normal user should not have to instanciate a Client by itself. However, in the future it will be useful for power-users to manage projects and connections.

Parameters:

Name Type Description Default credentials Optional[dict]

(optional) Credentials to connect

None project Optional[Project]

(optional) Project to connect to. If not specified, the client will connect to the default user's project.

None Source code in ydata/sdk/common/client/client.py
@typechecked\nclass Client(metaclass=SingletonClient):\n\"\"\"Main Client class used to abstract the connection to the backend.\n    A normal user should not have to instanciate a [`Client`][ydata.sdk.common.client.Client] by itself.\n    However, in the future it will be useful for power-users to manage projects and connections.\n    Args:\n        credentials (Optional[dict]): (optional) Credentials to connect\n        project (Optional[Project]): (optional) Project to connect to. If not specified, the client will connect to the default user's project.\n    \"\"\"\ncodes = codes\ndef __init__(self, credentials: Optional[Union[str, Dict]] = None, project: Optional[Project] = None, set_as_global: bool = False):\nself._base_url = environ.get(\"YDATA_BASE_URL\", DEFAULT_URL)\nself._scheme = 'https'\nself._headers = {'Authorization': credentials}\nself._http_client = httpClient(\nheaders=self._headers, timeout=Timeout(10, read=None))\nself._handshake()\nself._default_project = project or self._get_default_project(credentials)\nif set_as_global:\nself.__set_global()\ndef post(self, endpoint: str, data: Optional[Dict] = None, json: Optional[Dict] = None,\nproject: Project | None = None, files: Optional[Dict] = None, raise_for_status: bool = True) -> Response:\n\"\"\"POST request to the backend.\n        Args:\n            endpoint (str): POST endpoint\n            data (Optional[dict]): (optional) multipart form data\n            json (Optional[dict]): (optional) json data\n            files (Optional[dict]): (optional) files to be sent\n            raise_for_status (bool): raise an exception on error\n        Returns:\n            Response object\n        \"\"\"\nurl_data = self.__build_url(\nendpoint, data=data, json=json, files=files, project=project)\nresponse = self._http_client.post(**url_data)\nif response.status_code != Client.codes.OK and raise_for_status:\nself.__raise_for_status(response)\nreturn response\ndef get(self, endpoint: str, params: Optional[Dict] = None,\nproject: Project | None = None, cookies: Optional[Dict] = None, raise_for_status: bool = True) -> Response:\n\"\"\"GET request to the backend.\n        Args:\n            endpoint (str): GET endpoint\n            cookies (Optional[dict]): (optional) cookies data\n            raise_for_status (bool): raise an exception on error\n        Returns:\n            Response object\n        \"\"\"\nurl_data = self.__build_url(endpoint, params=params,\ncookies=cookies, project=project)\nresponse = self._http_client.get(**url_data)\nif response.status_code != Client.codes.OK and raise_for_status:\nself.__raise_for_status(response)\nreturn response\ndef get_static_file(self, endpoint: str, project: Project | None = None, raise_for_status: bool = True) -> Response:\n\"\"\"Retrieve a static file from the backend.\n        Args:\n            endpoint (str): GET endpoint\n            raise_for_status (bool): raise an exception on error\n        Returns:\n            Response object\n        \"\"\"\nurl_data = self.__build_url(endpoint, project=project)\nurl_data['url'] = f'{self._scheme}://{self._base_url}/static-content{endpoint}'\nresponse = self._http_client.get(**url_data)\nif response.status_code != Client.codes.OK and raise_for_status:\nself.__raise_for_status(response)\nreturn response\ndef _handshake(self):\n\"\"\"Client handshake.\n        It is used to determine is the client can connect with its\n        current authorization token.\n        \"\"\"\nresponse = self.get('/profiles', params={}, raise_for_status=False)\nif response.status_code == Client.codes.FOUND:\nparser = LinkExtractor()\nparser.feed(response.text)\nraise ClientHandshakeError(auth_link=parser.link)\ndef _get_default_project(self, token: str):\nresponse = self.get('/profiles/me', params={}, cookies={'access_token': token})\ndata: Dict = response.json()\nreturn data['myWorkspace']\ndef __build_url(self, endpoint: str, params: Optional[Dict] = None, data: Optional[Dict] = None,\njson: Optional[Dict] = None, project: Project | None = None, files: Optional[Dict] = None,\ncookies: Optional[Dict] = None) -> Dict:\n\"\"\"Build a request for the backend.\n        Args:\n            endpoint (str): backend endpoint\n            params (Optional[dict]): URL parameters\n            data (Optional[Project]): (optional) multipart form data\n            json (Optional[dict]): (optional) json data\n            files (Optional[dict]): (optional) files to be sent\n            cookies (Optional[dict]): (optional) cookies data\n        Returns:\n            dictionary containing the information to perform a request\n        \"\"\"\n_params = params if params is not None else {\n'ns': project or self._default_project\n}\nurl_data = {\n'url': f'{self._scheme}://{self._base_url}/api{endpoint}',\n'headers': self._headers,\n'params': _params,\n}\nif data is not None:\nurl_data['data'] = data\nif json is not None:\nurl_data['json'] = json\nif files is not None:\nurl_data['files'] = files\nif cookies is not None:\nurl_data['cookies'] = cookies\nreturn url_data\ndef __set_global(self) -> None:\n\"\"\"Sets a client instance as global.\"\"\"\n# If the client is stateful, close it gracefully!\nClient.GLOBAL_CLIENT = self\ndef __raise_for_status(self, response: Response) -> None:\n\"\"\"Raise an exception if the response is not OK.\n        When an exception is raised, we try to convert it to a ResponseError which is\n        a wrapper around a backend error. This usually gives enough context and provides\n        nice error message.\n        If it cannot be converted to ResponseError, it is re-raised.\n        Args:\n            response (Response): response to analyze\n        \"\"\"\ntry:\nresponse.raise_for_status()\nexcept HTTPStatusError as e:\nwith suppress(Exception):\ne = ResponseError(**response.json())\nraise e\n
"},{"location":"sdk/reference/api/common/client/#ydata.sdk.common.client.client.Client.__build_url","title":"__build_url(endpoint, params=None, data=None, json=None, project=None, files=None, cookies=None)","text":"

Build a request for the backend.

Parameters:

Name Type Description Default endpoint str

backend endpoint

required params Optional[dict]

URL parameters

None data Optional[Project]

(optional) multipart form data

None json Optional[dict]

(optional) json data

None files Optional[dict]

(optional) files to be sent

None cookies Optional[dict]

(optional) cookies data

None

Returns:

Type Description Dict

dictionary containing the information to perform a request

Source code in ydata/sdk/common/client/client.py
def __build_url(self, endpoint: str, params: Optional[Dict] = None, data: Optional[Dict] = None,\njson: Optional[Dict] = None, project: Project | None = None, files: Optional[Dict] = None,\ncookies: Optional[Dict] = None) -> Dict:\n\"\"\"Build a request for the backend.\n    Args:\n        endpoint (str): backend endpoint\n        params (Optional[dict]): URL parameters\n        data (Optional[Project]): (optional) multipart form data\n        json (Optional[dict]): (optional) json data\n        files (Optional[dict]): (optional) files to be sent\n        cookies (Optional[dict]): (optional) cookies data\n    Returns:\n        dictionary containing the information to perform a request\n    \"\"\"\n_params = params if params is not None else {\n'ns': project or self._default_project\n}\nurl_data = {\n'url': f'{self._scheme}://{self._base_url}/api{endpoint}',\n'headers': self._headers,\n'params': _params,\n}\nif data is not None:\nurl_data['data'] = data\nif json is not None:\nurl_data['json'] = json\nif files is not None:\nurl_data['files'] = files\nif cookies is not None:\nurl_data['cookies'] = cookies\nreturn url_data\n
"},{"location":"sdk/reference/api/common/client/#ydata.sdk.common.client.client.Client.__raise_for_status","title":"__raise_for_status(response)","text":"

Raise an exception if the response is not OK.

When an exception is raised, we try to convert it to a ResponseError which is a wrapper around a backend error. This usually gives enough context and provides nice error message.

If it cannot be converted to ResponseError, it is re-raised.

Parameters:

Name Type Description Default response Response

response to analyze

required Source code in ydata/sdk/common/client/client.py
def __raise_for_status(self, response: Response) -> None:\n\"\"\"Raise an exception if the response is not OK.\n    When an exception is raised, we try to convert it to a ResponseError which is\n    a wrapper around a backend error. This usually gives enough context and provides\n    nice error message.\n    If it cannot be converted to ResponseError, it is re-raised.\n    Args:\n        response (Response): response to analyze\n    \"\"\"\ntry:\nresponse.raise_for_status()\nexcept HTTPStatusError as e:\nwith suppress(Exception):\ne = ResponseError(**response.json())\nraise e\n
"},{"location":"sdk/reference/api/common/client/#ydata.sdk.common.client.client.Client.__set_global","title":"__set_global()","text":"

Sets a client instance as global.

Source code in ydata/sdk/common/client/client.py
def __set_global(self) -> None:\n\"\"\"Sets a client instance as global.\"\"\"\n# If the client is stateful, close it gracefully!\nClient.GLOBAL_CLIENT = self\n
"},{"location":"sdk/reference/api/common/client/#ydata.sdk.common.client.client.Client.get","title":"get(endpoint, params=None, project=None, cookies=None, raise_for_status=True)","text":"

GET request to the backend.

Parameters:

Name Type Description Default endpoint str

GET endpoint

required cookies Optional[dict]

(optional) cookies data

None raise_for_status bool

raise an exception on error

True

Returns:

Type Description Response

Response object

Source code in ydata/sdk/common/client/client.py
def get(self, endpoint: str, params: Optional[Dict] = None,\nproject: Project | None = None, cookies: Optional[Dict] = None, raise_for_status: bool = True) -> Response:\n\"\"\"GET request to the backend.\n    Args:\n        endpoint (str): GET endpoint\n        cookies (Optional[dict]): (optional) cookies data\n        raise_for_status (bool): raise an exception on error\n    Returns:\n        Response object\n    \"\"\"\nurl_data = self.__build_url(endpoint, params=params,\ncookies=cookies, project=project)\nresponse = self._http_client.get(**url_data)\nif response.status_code != Client.codes.OK and raise_for_status:\nself.__raise_for_status(response)\nreturn response\n
"},{"location":"sdk/reference/api/common/client/#ydata.sdk.common.client.client.Client.get_static_file","title":"get_static_file(endpoint, project=None, raise_for_status=True)","text":"

Retrieve a static file from the backend.

Parameters:

Name Type Description Default endpoint str

GET endpoint

required raise_for_status bool

raise an exception on error

True

Returns:

Type Description Response

Response object

Source code in ydata/sdk/common/client/client.py
def get_static_file(self, endpoint: str, project: Project | None = None, raise_for_status: bool = True) -> Response:\n\"\"\"Retrieve a static file from the backend.\n    Args:\n        endpoint (str): GET endpoint\n        raise_for_status (bool): raise an exception on error\n    Returns:\n        Response object\n    \"\"\"\nurl_data = self.__build_url(endpoint, project=project)\nurl_data['url'] = f'{self._scheme}://{self._base_url}/static-content{endpoint}'\nresponse = self._http_client.get(**url_data)\nif response.status_code != Client.codes.OK and raise_for_status:\nself.__raise_for_status(response)\nreturn response\n
"},{"location":"sdk/reference/api/common/client/#ydata.sdk.common.client.client.Client.post","title":"post(endpoint, data=None, json=None, project=None, files=None, raise_for_status=True)","text":"

POST request to the backend.

Parameters:

Name Type Description Default endpoint str

POST endpoint

required data Optional[dict]

(optional) multipart form data

None json Optional[dict]

(optional) json data

None files Optional[dict]

(optional) files to be sent

None raise_for_status bool

raise an exception on error

True

Returns:

Type Description Response

Response object

Source code in ydata/sdk/common/client/client.py
def post(self, endpoint: str, data: Optional[Dict] = None, json: Optional[Dict] = None,\nproject: Project | None = None, files: Optional[Dict] = None, raise_for_status: bool = True) -> Response:\n\"\"\"POST request to the backend.\n    Args:\n        endpoint (str): POST endpoint\n        data (Optional[dict]): (optional) multipart form data\n        json (Optional[dict]): (optional) json data\n        files (Optional[dict]): (optional) files to be sent\n        raise_for_status (bool): raise an exception on error\n    Returns:\n        Response object\n    \"\"\"\nurl_data = self.__build_url(\nendpoint, data=data, json=json, files=files, project=project)\nresponse = self._http_client.post(**url_data)\nif response.status_code != Client.codes.OK and raise_for_status:\nself.__raise_for_status(response)\nreturn response\n
"},{"location":"sdk/reference/api/common/types/","title":"Types","text":""},{"location":"sdk/reference/api/connectors/connector/","title":"Connector","text":"

Bases: ModelFactoryMixin

A Connector allows to connect and access data stored in various places. The list of available connectors can be found here.

Parameters:

Name Type Description Default connector_type Union[ConnectorType, str]

Type of the connector to be created

None credentials dict

Connector credentials

None name Optional[str]

(optional) Connector name

None client Client

(optional) Client to connect to the backend

None

Attributes:

Name Type Description uid UID

UID fo the connector instance (creating internally)

type ConnectorType

Type of the connector

Source code in ydata/sdk/connectors/connector.py
class Connector(ModelFactoryMixin):\n\"\"\"A [`Connector`][ydata.sdk.connectors.Connector] allows to connect and\n    access data stored in various places. The list of available connectors can\n    be found [here][ydata.sdk.connectors.ConnectorType].\n    Arguments:\n        connector_type (Union[ConnectorType, str]): Type of the connector to be created\n        credentials (dict): Connector credentials\n        name (Optional[str]): (optional) Connector name\n        client (Client): (optional) Client to connect to the backend\n    Attributes:\n        uid (UID): UID fo the connector instance (creating internally)\n        type (ConnectorType): Type of the connector\n    \"\"\"\ndef __init__(self, connector_type: Union[ConnectorType, str] = None, credentials: Optional[Dict] = None,  name: Optional[str] = None, client: Optional[Client] = None):\nself._init_common(client=client)\nself._model: Optional[mConnector] = self._create_model(\nconnector_type, credentials, name, client=client)\n@init_client\ndef _init_common(self, client: Optional[Client] = None):\nself._client = client\nself._logger = create_logger(__name__, level=LOG_LEVEL)\n@property\ndef uid(self) -> UID:\nreturn self._model.uid\n@property\ndef type(self) -> str:\nreturn self._model.type\n@staticmethod\n@init_client\ndef get(uid: UID, client: Optional[Client] = None) -> \"Connector\":\n\"\"\"Get an existing connector.\n        Arguments:\n            uid (UID): Connector identifier\n            client (Client): (optional) Client to connect to the backend\n        Returns:\n            Connector\n        \"\"\"\nconnectors: ConnectorsList = Connector.list(client=client)\ndata = connectors.get_by_uid(uid)\nmodel = mConnector(**data)\nconnector = ModelFactoryMixin._init_from_model_data(Connector, model)\nreturn connector\n@staticmethod\ndef _init_connector_type(connector_type: Union[ConnectorType, str]) -> ConnectorType:\nif isinstance(connector_type, str):\ntry:\nconnector_type = ConnectorType(connector_type)\nexcept Exception:\nc_list = \", \".join([c.value for c in ConnectorType])\nraise InvalidConnectorError(\nf\"ConnectorType '{connector_type}' does not exist.\\nValid connector types are: {c_list}.\")\nreturn connector_type\n@staticmethod\ndef _init_credentials(connector_type: ConnectorType, credentials: Union[str, Path, Dict, Credentials]) -> Credentials:\n_credentials = None\nif isinstance(credentials, str):\ncredentials = Path(credentials)\nif isinstance(credentials, Path):\ntry:\n_credentials = json_loads(credentials.open().read())\nexcept Exception:\nraise CredentialTypeError(\n'Could not read the credentials. Please, check your path or credentials structure.')\ntry:\nfrom ydata.sdk.connectors._models.connector_map import TYPE_TO_CLASS\ncredential_cls = TYPE_TO_CLASS.get(connector_type.value)\n_credentials = credential_cls(**_credentials)\nexcept Exception:\nraise CredentialTypeError(\n\"Could not create the credentials. Verify the path or the structure your credentials.\")\nreturn _credentials\n@staticmethod\ndef create(connector_type: Union[ConnectorType, str], credentials: Union[str, Path, Dict, Credentials], name: Optional[str] = None, client: Optional[Client] = None) -> \"Connector\":\n\"\"\"Create a new connector.\n        Arguments:\n            connector_type (Union[ConnectorType, str]): Type of the connector to be created\n            credentials (dict): Connector credentials\n            name (Optional[str]): (optional) Connector name\n            client (Client): (optional) Client to connect to the backend\n        Returns:\n            New connector\n        \"\"\"\nmodel = Connector._create_model(\nconnector_type=connector_type, credentials=credentials, name=name, client=client)\nconnector = ModelFactoryMixin._init_from_model_data(\nConnector, model)\nreturn connector\n@classmethod\n@init_client\ndef _create_model(cls, connector_type: Union[ConnectorType, str], credentials: Union[str, Path, Dict, Credentials], name: Optional[str] = None, client: Optional[Client] = None) -> mConnector:\n_name = name if name is not None else str(uuid4())\n_connector_type = Connector._init_connector_type(connector_type)\n_credentials = Connector._init_credentials(_connector_type, credentials)\npayload = {\n\"type\": _connector_type.value,\n\"credentials\": _credentials.dict(by_alias=True),\n\"name\": _name\n}\nresponse = client.post('/connector/', json=payload)\ndata: list = response.json()\nreturn mConnector(**data)\n@staticmethod\n@init_client\ndef list(client: Optional[Client] = None) -> ConnectorsList:\n\"\"\"List the connectors instances.\n        Arguments:\n            client (Client): (optional) Client to connect to the backend\n        Returns:\n            List of connectors\n        \"\"\"\nresponse = client.get('/connector')\ndata: list = response.json()\nreturn ConnectorsList(data)\ndef __repr__(self):\nreturn self._model.__repr__()\n
"},{"location":"sdk/reference/api/connectors/connector/#ydata.sdk.connectors.connector.Connector.create","title":"create(connector_type, credentials, name=None, client=None) staticmethod","text":"

Create a new connector.

Parameters:

Name Type Description Default connector_type Union[ConnectorType, str]

Type of the connector to be created

required credentials dict

Connector credentials

required name Optional[str]

(optional) Connector name

None client Client

(optional) Client to connect to the backend

None

Returns:

Type Description Connector

New connector

Source code in ydata/sdk/connectors/connector.py
@staticmethod\ndef create(connector_type: Union[ConnectorType, str], credentials: Union[str, Path, Dict, Credentials], name: Optional[str] = None, client: Optional[Client] = None) -> \"Connector\":\n\"\"\"Create a new connector.\n    Arguments:\n        connector_type (Union[ConnectorType, str]): Type of the connector to be created\n        credentials (dict): Connector credentials\n        name (Optional[str]): (optional) Connector name\n        client (Client): (optional) Client to connect to the backend\n    Returns:\n        New connector\n    \"\"\"\nmodel = Connector._create_model(\nconnector_type=connector_type, credentials=credentials, name=name, client=client)\nconnector = ModelFactoryMixin._init_from_model_data(\nConnector, model)\nreturn connector\n
"},{"location":"sdk/reference/api/connectors/connector/#ydata.sdk.connectors.connector.Connector.get","title":"get(uid, client=None) staticmethod","text":"

Get an existing connector.

Parameters:

Name Type Description Default uid UID

Connector identifier

required client Client

(optional) Client to connect to the backend

None

Returns:

Type Description Connector

Connector

Source code in ydata/sdk/connectors/connector.py
@staticmethod\n@init_client\ndef get(uid: UID, client: Optional[Client] = None) -> \"Connector\":\n\"\"\"Get an existing connector.\n    Arguments:\n        uid (UID): Connector identifier\n        client (Client): (optional) Client to connect to the backend\n    Returns:\n        Connector\n    \"\"\"\nconnectors: ConnectorsList = Connector.list(client=client)\ndata = connectors.get_by_uid(uid)\nmodel = mConnector(**data)\nconnector = ModelFactoryMixin._init_from_model_data(Connector, model)\nreturn connector\n
"},{"location":"sdk/reference/api/connectors/connector/#ydata.sdk.connectors.connector.Connector.list","title":"list(client=None) staticmethod","text":"

List the connectors instances.

Parameters:

Name Type Description Default client Client

(optional) Client to connect to the backend

None

Returns:

Type Description ConnectorsList

List of connectors

Source code in ydata/sdk/connectors/connector.py
@staticmethod\n@init_client\ndef list(client: Optional[Client] = None) -> ConnectorsList:\n\"\"\"List the connectors instances.\n    Arguments:\n        client (Client): (optional) Client to connect to the backend\n    Returns:\n        List of connectors\n    \"\"\"\nresponse = client.get('/connector')\ndata: list = response.json()\nreturn ConnectorsList(data)\n
"},{"location":"sdk/reference/api/connectors/connector/#connectortype","title":"ConnectorType","text":"

Bases: Enum

"},{"location":"sdk/reference/api/connectors/connector/#ydata.sdk.connectors.ConnectorType.AWS_S3","title":"AWS_S3 = 'aws-s3' class-attribute instance-attribute","text":"

AWS S3 connector

"},{"location":"sdk/reference/api/connectors/connector/#ydata.sdk.connectors.ConnectorType.AZURE_BLOB","title":"AZURE_BLOB = 'azure-blob' class-attribute instance-attribute","text":"

Azure Blob connector

"},{"location":"sdk/reference/api/connectors/connector/#ydata.sdk.connectors.ConnectorType.AZURE_SQL","title":"AZURE_SQL = 'azure-sql' class-attribute instance-attribute","text":"

AzureSQL connector

"},{"location":"sdk/reference/api/connectors/connector/#ydata.sdk.connectors.ConnectorType.BIGQUERY","title":"BIGQUERY = 'google-bigquery' class-attribute instance-attribute","text":"

BigQuery connector

"},{"location":"sdk/reference/api/connectors/connector/#ydata.sdk.connectors.ConnectorType.FILE","title":"FILE = 'file' class-attribute instance-attribute","text":"

File connector (placeholder)

"},{"location":"sdk/reference/api/connectors/connector/#ydata.sdk.connectors.ConnectorType.GCS","title":"GCS = 'gcs' class-attribute instance-attribute","text":"

Google Cloud Storage connector

"},{"location":"sdk/reference/api/connectors/connector/#ydata.sdk.connectors.ConnectorType.MYSQL","title":"MYSQL = 'mysql' class-attribute instance-attribute","text":"

MySQL connector

"},{"location":"sdk/reference/api/connectors/connector/#ydata.sdk.connectors.ConnectorType.SNOWFLAKE","title":"SNOWFLAKE = 'snowflake' class-attribute instance-attribute","text":"

Snowflake connector

"},{"location":"sdk/reference/api/datasources/datasource/","title":"DataSource","text":"

Bases: ModelFactoryMixin

A DataSource represents a dataset to be used by a Synthesizer as training data.

Parameters:

Name Type Description Default connector Connector

Connector from which the datasource is created

required datatype Optional[Union[DataSourceType, str]]

(optional) DataSource type

TABULAR name Optional[str]

(optional) DataSource name

None wait_for_metadata bool

If True, wait until the metadata is fully calculated

True client Client

(optional) Client to connect to the backend

None **config

Datasource specific configuration

{}

Attributes:

Name Type Description uid UID

UID fo the datasource instance

datatype DataSourceType

Data source type

status Status

Status of the datasource

metadata Metadata

Metadata associated to the datasource

Source code in ydata/sdk/datasources/datasource.py
class DataSource(ModelFactoryMixin):\n\"\"\"A [`DataSource`][ydata.sdk.datasources.DataSource] represents a dataset\n    to be used by a Synthesizer as training data.\n    Arguments:\n        connector (Connector): Connector from which the datasource is created\n        datatype (Optional[Union[DataSourceType, str]]): (optional) DataSource type\n        name (Optional[str]): (optional) DataSource name\n        wait_for_metadata (bool): If `True`, wait until the metadata is fully calculated\n        client (Client): (optional) Client to connect to the backend\n        **config: Datasource specific configuration\n    Attributes:\n        uid (UID): UID fo the datasource instance\n        datatype (DataSourceType): Data source type\n        status (Status): Status of the datasource\n        metadata (Metadata): Metadata associated to the datasource\n    \"\"\"\ndef __init__(self, connector: Connector, datatype: Optional[Union[DataSourceType, str]] = DataSourceType.TABULAR, name: Optional[str] = None, wait_for_metadata: bool = True, client: Optional[Client] = None, **config):\ndatasource_type = CONNECTOR_TO_DATASOURCE.get(connector.type)\nself._init_common(client=client)\nself._model: Optional[mDataSource] = self._create_model(\nconnector=connector, datasource_type=datasource_type, datatype=datatype, config=config, name=name, client=self._client)\nif wait_for_metadata:\nself._model = DataSource._wait_for_metadata(self)._model\n@init_client\ndef _init_common(self, client: Optional[Client] = None):\nself._client = client\nself._logger = create_logger(__name__, level=LOG_LEVEL)\n@property\ndef uid(self) -> UID:\nreturn self._model.uid\n@property\ndef datatype(self) -> DataSourceType:\nreturn self._model.datatype\n@property\ndef status(self) -> Status:\ntry:\nself._model = self.get(self._model.uid, self._client)._model\nreturn self._model.status\nexcept Exception:  # noqa: PIE786\nreturn Status.UNKNOWN\n@property\ndef metadata(self) -> Metadata:\nreturn self._model.metadata\n@staticmethod\n@init_client\ndef list(client: Optional[Client] = None) -> DataSourceList:\n\"\"\"List the  [`DataSource`][ydata.sdk.datasources.DataSource]\n        instances.\n        Arguments:\n            client (Client): (optional) Client to connect to the backend\n        Returns:\n            List of datasources\n        \"\"\"\ndef __process_data(data: list) -> list:\nto_del = ['metadata']\nfor e in data:\nfor k in to_del:\ne.pop(k, None)\nreturn data\nresponse = client.get('/datasource')\ndata: list = response.json()\ndata = __process_data(data)\nreturn DataSourceList(data)\n@staticmethod\n@init_client\ndef get(uid: UID, client: Optional[Client] = None) -> \"DataSource\":\n\"\"\"Get an existing [`DataSource`][ydata.sdk.datasources.DataSource].\n        Arguments:\n            uid (UID): DataSource identifier\n            client (Client): (optional) Client to connect to the backend\n        Returns:\n            DataSource\n        \"\"\"\nresponse = client.get(f'/datasource/{uid}')\ndata: list = response.json()\ndatasource_type = CONNECTOR_TO_DATASOURCE.get(\nConnectorType(data['connector']['type']))\nmodel = DataSource._model_from_api(data, datasource_type)\ndatasource = ModelFactoryMixin._init_from_model_data(DataSource, model)\nreturn datasource\n@classmethod\ndef create(cls, connector: Connector, datatype: Optional[Union[DataSourceType, str]] = DataSourceType.TABULAR, name: Optional[str] = None, wait_for_metadata: bool = True, client: Optional[Client] = None, **config) -> \"DataSource\":\n\"\"\"Create a new [`DataSource`][ydata.sdk.datasources.DataSource].\n        Arguments:\n            connector (Connector): Connector from which the datasource is created\n            datatype (Optional[Union[DataSourceType, str]]): (optional) DataSource type\n            name (Optional[str]): (optional) DataSource name\n            wait_for_metadata (bool): If `True`, wait until the metadata is fully calculated\n            client (Client): (optional) Client to connect to the backend\n            **config: Datasource specific configuration\n        Returns:\n            DataSource\n        \"\"\"\ndatasource_type = CONNECTOR_TO_DATASOURCE.get(connector.type)\nreturn cls._create(connector=connector, datasource_type=datasource_type, datatype=datatype, config=config, name=name, wait_for_metadata=wait_for_metadata, client=client)\n@classmethod\ndef _create(cls, connector: Connector, datasource_type: Type[mDataSource], datatype: Optional[Union[DataSourceType, str]] = DataSourceType.TABULAR, config: Optional[Dict] = None, name: Optional[str] = None, wait_for_metadata: bool = True, client: Optional[Client] = None) -> \"DataSource\":\nmodel = DataSource._create_model(\nconnector, datasource_type, datatype, config, name, client)\ndatasource = ModelFactoryMixin._init_from_model_data(DataSource, model)\nif wait_for_metadata:\ndatasource._model = DataSource._wait_for_metadata(datasource)._model\nreturn datasource\n@classmethod\n@init_client\ndef _create_model(cls, connector: Connector, datasource_type: Type[mDataSource], datatype: Optional[Union[DataSourceType, str]] = DataSourceType.TABULAR, config: Optional[Dict] = None, name: Optional[str] = None, client: Optional[Client] = None) -> mDataSource:\n_name = name if name is not None else str(uuid4())\n_config = config if config is not None else {}\npayload = {\n\"name\": _name,\n\"connector\": {\n\"uid\": connector.uid,\n\"type\": connector.type.value\n},\n\"dataType\": datatype.value\n}\nif connector.type != ConnectorType.FILE:\n_config = datasource_type(**config).to_payload()\npayload.update(_config)\nresponse = client.post('/datasource/', json=payload)\ndata: list = response.json()\nreturn DataSource._model_from_api(data, datasource_type)\n@staticmethod\ndef _wait_for_metadata(datasource):\nlogger = create_logger(__name__, level=LOG_LEVEL)\nwhile datasource.status not in [Status.AVAILABLE, Status.FAILED, Status.UNAVAILABLE]:\nlogger.info(f'Calculating metadata [{datasource.status}]')\ndatasource = DataSource.get(uid=datasource.uid, client=datasource._client)\nsleep(BACKOFF)\nreturn datasource\n@staticmethod\ndef _resolve_api_status(api_status: Dict) -> Status:\nstatus = Status(api_status.get('state', Status.UNKNOWN.name))\nvalidation = ValidationState(api_status.get('validation', {}).get(\n'state', ValidationState.UNKNOWN.name))\nif validation == ValidationState.FAILED:\nstatus = Status.FAILED\nreturn status\n@staticmethod\ndef _model_from_api(data: Dict, datasource_type: Type[mDataSource]) -> mDataSource:\ndata['datatype'] = data.pop('dataType')\ndata['state'] = data['status']\ndata['status'] = DataSource._resolve_api_status(data['status'])\ndata = filter_dict(datasource_type, data)\nmodel = datasource_type(**data)\nreturn model\ndef __repr__(self):\nreturn self._model.__repr__()\n
"},{"location":"sdk/reference/api/datasources/datasource/#ydata.sdk.datasources.datasource.DataSource.create","title":"create(connector, datatype=DataSourceType.TABULAR, name=None, wait_for_metadata=True, client=None, **config) classmethod","text":"

Create a new DataSource.

Parameters:

Name Type Description Default connector Connector

Connector from which the datasource is created

required datatype Optional[Union[DataSourceType, str]]

(optional) DataSource type

TABULAR name Optional[str]

(optional) DataSource name

None wait_for_metadata bool

If True, wait until the metadata is fully calculated

True client Client

(optional) Client to connect to the backend

None **config

Datasource specific configuration

{}

Returns:

Type Description DataSource

DataSource

Source code in ydata/sdk/datasources/datasource.py
@classmethod\ndef create(cls, connector: Connector, datatype: Optional[Union[DataSourceType, str]] = DataSourceType.TABULAR, name: Optional[str] = None, wait_for_metadata: bool = True, client: Optional[Client] = None, **config) -> \"DataSource\":\n\"\"\"Create a new [`DataSource`][ydata.sdk.datasources.DataSource].\n    Arguments:\n        connector (Connector): Connector from which the datasource is created\n        datatype (Optional[Union[DataSourceType, str]]): (optional) DataSource type\n        name (Optional[str]): (optional) DataSource name\n        wait_for_metadata (bool): If `True`, wait until the metadata is fully calculated\n        client (Client): (optional) Client to connect to the backend\n        **config: Datasource specific configuration\n    Returns:\n        DataSource\n    \"\"\"\ndatasource_type = CONNECTOR_TO_DATASOURCE.get(connector.type)\nreturn cls._create(connector=connector, datasource_type=datasource_type, datatype=datatype, config=config, name=name, wait_for_metadata=wait_for_metadata, client=client)\n
"},{"location":"sdk/reference/api/datasources/datasource/#ydata.sdk.datasources.datasource.DataSource.get","title":"get(uid, client=None) staticmethod","text":"

Get an existing DataSource.

Parameters:

Name Type Description Default uid UID

DataSource identifier

required client Client

(optional) Client to connect to the backend

None

Returns:

Type Description DataSource

DataSource

Source code in ydata/sdk/datasources/datasource.py
@staticmethod\n@init_client\ndef get(uid: UID, client: Optional[Client] = None) -> \"DataSource\":\n\"\"\"Get an existing [`DataSource`][ydata.sdk.datasources.DataSource].\n    Arguments:\n        uid (UID): DataSource identifier\n        client (Client): (optional) Client to connect to the backend\n    Returns:\n        DataSource\n    \"\"\"\nresponse = client.get(f'/datasource/{uid}')\ndata: list = response.json()\ndatasource_type = CONNECTOR_TO_DATASOURCE.get(\nConnectorType(data['connector']['type']))\nmodel = DataSource._model_from_api(data, datasource_type)\ndatasource = ModelFactoryMixin._init_from_model_data(DataSource, model)\nreturn datasource\n
"},{"location":"sdk/reference/api/datasources/datasource/#ydata.sdk.datasources.datasource.DataSource.list","title":"list(client=None) staticmethod","text":"

List the DataSource instances.

Parameters:

Name Type Description Default client Client

(optional) Client to connect to the backend

None

Returns:

Type Description DataSourceList

List of datasources

Source code in ydata/sdk/datasources/datasource.py
@staticmethod\n@init_client\ndef list(client: Optional[Client] = None) -> DataSourceList:\n\"\"\"List the  [`DataSource`][ydata.sdk.datasources.DataSource]\n    instances.\n    Arguments:\n        client (Client): (optional) Client to connect to the backend\n    Returns:\n        List of datasources\n    \"\"\"\ndef __process_data(data: list) -> list:\nto_del = ['metadata']\nfor e in data:\nfor k in to_del:\ne.pop(k, None)\nreturn data\nresponse = client.get('/datasource')\ndata: list = response.json()\ndata = __process_data(data)\nreturn DataSourceList(data)\n
"},{"location":"sdk/reference/api/datasources/datasource/#status","title":"Status","text":"

Bases: StringEnum

Represent the status of a DataSource.

"},{"location":"sdk/reference/api/datasources/datasource/#ydata.sdk.datasources.Status.AVAILABLE","title":"AVAILABLE = 'available' class-attribute instance-attribute","text":"

The DataSource is available and ready to be used.

"},{"location":"sdk/reference/api/datasources/datasource/#ydata.sdk.datasources.Status.DELETED","title":"DELETED = 'deleted' class-attribute instance-attribute","text":"

The DataSource is to be deleted or has been deleted.

"},{"location":"sdk/reference/api/datasources/datasource/#ydata.sdk.datasources.Status.FAILED","title":"FAILED = 'failed' class-attribute instance-attribute","text":"

The DataSource preparation or validation has failed.

"},{"location":"sdk/reference/api/datasources/datasource/#ydata.sdk.datasources.Status.PREPARING","title":"PREPARING = 'preparing' class-attribute instance-attribute","text":"

The DataSource is being prepared.

"},{"location":"sdk/reference/api/datasources/datasource/#ydata.sdk.datasources.Status.UNAVAILABLE","title":"UNAVAILABLE = 'unavailable' class-attribute instance-attribute","text":"

The DataSource is unavailable at the moment.

"},{"location":"sdk/reference/api/datasources/datasource/#ydata.sdk.datasources.Status.UNKNOWN","title":"UNKNOWN = 'unknown' class-attribute instance-attribute","text":"

The DataSource status could not be retrieved.

"},{"location":"sdk/reference/api/datasources/datasource/#ydata.sdk.datasources.Status.VALIDATING","title":"VALIDATING = 'validating' class-attribute instance-attribute","text":"

The DataSource is being validated.

"},{"location":"sdk/reference/api/datasources/datasource/#datasourcetype","title":"DataSourceType","text":"

Bases: StringEnum

"},{"location":"sdk/reference/api/datasources/datasource/#ydata.sdk.datasources.DataSourceType.TABULAR","title":"TABULAR = 'tabular' class-attribute instance-attribute","text":"

The DataSource is tabular (i.e. it does not have a temporal dimension).

"},{"location":"sdk/reference/api/datasources/datasource/#ydata.sdk.datasources.DataSourceType.TIMESERIES","title":"TIMESERIES = 'timeseries' class-attribute instance-attribute","text":"

The DataSource has a temporal dimension.

"},{"location":"sdk/reference/api/datasources/metadata/","title":"Metadata","text":"

Bases: BaseModel

The Metadata object contains descriptive information about a.

DataSource

Attributes:

Name Type Description columns List[Column]

columns information

"},{"location":"sdk/reference/api/synthesizers/base/","title":"Synthesizer","text":"

Bases: ABC, ModelFactoryMixin

Main synthesizer class.

This class cannot be directly instanciated because of the specificities between RegularSynthesizer and TimeSeriesSynthesizer sample methods.

"},{"location":"sdk/reference/api/synthesizers/base/#ydata.sdk.synthesizers.synthesizer.BaseSynthesizer--methods","title":"Methods","text":"
  • fit: train a synthesizer instance.
  • sample: request synthetic data.
  • status: current status of the synthesizer instance.
Note

The synthesizer instance is created in the backend only when the fit method is called.

Parameters:

Name Type Description Default client Client

(optional) Client to connect to the backend

None Source code in ydata/sdk/synthesizers/synthesizer.py
@typechecked\nclass BaseSynthesizer(ABC, ModelFactoryMixin):\n\"\"\"Main synthesizer class.\n    This class cannot be directly instanciated because of the specificities between [`RegularSynthesizer`][ydata.sdk.synthesizers.RegularSynthesizer] and [`TimeSeriesSynthesizer`][ydata.sdk.synthesizers.TimeSeriesSynthesizer] `sample` methods.\n    Methods\n    -------\n    - `fit`: train a synthesizer instance.\n    - `sample`: request synthetic data.\n    - `status`: current status of the synthesizer instance.\n    Note:\n            The synthesizer instance is created in the backend only when the `fit` method is called.\n    Arguments:\n        client (Client): (optional) Client to connect to the backend\n    \"\"\"\ndef __init__(self, uid: UID | None = None, name: str | None = None, project: Project | None = None, client: Client | None = None):\nself._init_common(client=client)\nself._model = mSynthesizer(uid=uid, name=name or str(\nuuid4())) if uid or project else None\nself.__project = project\n@init_client\ndef _init_common(self, client: Optional[Client] = None):\nself._client = client\nself._logger = create_logger(__name__, level=LOG_LEVEL)\ndef fit(self, X: Union[DataSource, pdDataFrame],\nprivacy_level: PrivacyLevel = PrivacyLevel.HIGH_FIDELITY,\ndatatype: Optional[Union[DataSourceType, str]] = None,\nsortbykey: Optional[Union[str, List[str]]] = None,\nentities: Optional[Union[str, List[str]]] = None,\ngenerate_cols: Optional[List[str]] = None,\nexclude_cols: Optional[List[str]] = None,\ndtypes: Optional[Dict[str, Union[str, DataType]]] = None,\ntarget: Optional[str] = None,\nanonymize: Optional[dict] = None,\ncondition_on: Optional[List[str]] = None) -> None:\n\"\"\"Fit the synthesizer.\n        The synthesizer accepts as training dataset either a pandas [`DataFrame`][pandas.DataFrame] directly or a YData [`DataSource`][ydata.sdk.datasources.DataSource].\n        When the training dataset is a pandas [`DataFrame`][pandas.DataFrame], the argument `datatype` is required as it cannot be deduced.\n        The argument`sortbykey` is mandatory for [`TimeSeries`][ydata.sdk.datasources.DataSourceType.TIMESERIES].\n        By default, if `generate_cols` or `exclude_cols` are not specified, all columns are generated by the synthesizer.\n        The argument `exclude_cols` has precedence over `generate_cols`, i.e. a column `col` will not be generated if it is in both list.\n        Arguments:\n            X (Union[DataSource, pandas.DataFrame]): Training dataset\n            privacy_level (PrivacyLevel): Synthesizer privacy level (defaults to high fidelity)\n            datatype (Optional[Union[DataSourceType, str]]): (optional) Dataset datatype - required if `X` is a [`pandas.DataFrame`][pandas.DataFrame]\n            sortbykey (Union[str, List[str]]): (optional) column(s) to use to sort timeseries datasets\n            entities (Union[str, List[str]]): (optional) columns representing entities ID\n            generate_cols (List[str]): (optional) columns that should be synthesized\n            exclude_cols (List[str]): (optional) columns that should not be synthesized\n            dtypes (Dict[str, Union[str, DataType]]): (optional) datatype mapping that will overwrite the datasource metadata column datatypes\n            target (Optional[str]): (optional) Target for the dataset\n            name (Optional[str]): (optional) Synthesizer instance name\n            anonymize (Optional[str]): (optional) fields to anonymize and the anonymization strategy\n            condition_on: (Optional[List[str]]): (optional) list of features to condition upon\n        \"\"\"\nif self._is_initialized():\nraise AlreadyFittedError()\n_datatype = DataSourceType(datatype) if isinstance(\nX, pdDataFrame) else DataSourceType(X.datatype)\ndataset_attrs = self._init_datasource_attributes(\nsortbykey, entities, generate_cols, exclude_cols, dtypes)\nself._validate_datasource_attributes(X, dataset_attrs, _datatype, target)\n# If the training data is a pandas dataframe, we first need to create a data source and then the instance\nif isinstance(X, pdDataFrame):\nif X.empty:\nraise EmptyDataError(\"The DataFrame is empty\")\n_X = LocalDataSource(source=X, datatype=_datatype, client=self._client)\nelse:\nif datatype != _datatype:\nwarn(\"When the training data is a DataSource, the argument `datatype` is ignored.\",\nDataSourceTypeWarning)\n_X = X\nif _X.status != dsStatus.AVAILABLE:\nraise DataSourceNotAvailableError(\nf\"The datasource '{_X.uid}' is not available (status = {_X.status.value})\")\nif isinstance(dataset_attrs, dict):\ndataset_attrs = DataSourceAttrs(**dataset_attrs)\nself._fit_from_datasource(\nX=_X, dataset_attrs=dataset_attrs, target=target,\nanonymize=anonymize, privacy_level=privacy_level, condition_on=condition_on)\n@staticmethod\ndef _init_datasource_attributes(\nsortbykey: Optional[Union[str, List[str]]],\nentities: Optional[Union[str, List[str]]],\ngenerate_cols: Optional[List[str]],\nexclude_cols: Optional[List[str]],\ndtypes: Optional[Dict[str, Union[str, DataType]]]) -> DataSourceAttrs:\ndataset_attrs = {\n'sortbykey': sortbykey if sortbykey is not None else [],\n'entities': entities if entities is not None else [],\n'generate_cols': generate_cols if generate_cols is not None else [],\n'exclude_cols': exclude_cols if exclude_cols is not None else [],\n'dtypes': {k: DataType(v) for k, v in dtypes.items()} if dtypes is not None else {}\n}\nreturn DataSourceAttrs(**dataset_attrs)\n@staticmethod\ndef _validate_datasource_attributes(X: Union[DataSource, pdDataFrame], dataset_attrs: DataSourceAttrs, datatype: DataSourceType, target: Optional[str]):\ncolumns = []\nif isinstance(X, pdDataFrame):\ncolumns = X.columns\nif datatype is None:\nraise DataTypeMissingError(\n\"Argument `datatype` is mandatory for pandas.DataFrame training data\")\ndatatype = DataSourceType(datatype)\nelse:\ncolumns = [c.name for c in X.metadata.columns]\nif target is not None and target not in columns:\nraise DataSourceAttrsError(\n\"Invalid target: column '{target}' does not exist\")\nif datatype == DataSourceType.TIMESERIES:\nif not dataset_attrs.sortbykey:\nraise DataSourceAttrsError(\n\"The argument `sortbykey` is mandatory for timeseries datasource.\")\ninvalid_fields = {}\nfor field, v in dataset_attrs.dict().items():\nfield_columns = v if field != 'dtypes' else v.keys()\nnot_in_cols = [c for c in field_columns if c not in columns]\nif len(not_in_cols) > 0:\ninvalid_fields[field] = not_in_cols\nif len(invalid_fields) > 0:\nerror_msgs = [\"\\t- Field '{}': columns {} do not exist\".format(\nf, ', '.join(v)) for f, v in invalid_fields.items()]\nraise DataSourceAttrsError(\n\"The dataset attributes are invalid:\\n {}\".format('\\n'.join(error_msgs)))\n@staticmethod\ndef _metadata_to_payload(\ndatatype: DataSourceType, ds_metadata: Metadata,\ndataset_attrs: Optional[DataSourceAttrs] = None, target: str | None = None\n) -> dict:\n\"\"\"Transform a the metadata and dataset attributes into a valid\n        payload.\n        Arguments:\n            datatype (DataSourceType): datasource type\n            ds_metadata (Metadata): datasource metadata object\n            dataset_attrs ( Optional[DataSourceAttrs] ): (optional) Dataset attributes\n            target (Optional[str]): (optional) target column name\n        Returns:\n            metadata payload dictionary\n        \"\"\"\ncolumns = [\n{\n'name': c.name,\n'generation': True and c.name not in dataset_attrs.exclude_cols,\n'dataType': DataType(dataset_attrs.dtypes[c.name]).value if c.name in dataset_attrs.dtypes else c.datatype,\n'varType': c.vartype,\n}\nfor c in ds_metadata.columns]\nmetadata = {\n'columns': columns,\n'target': target\n}\nif dataset_attrs is not None:\nif datatype == DataSourceType.TIMESERIES:\nmetadata['sortBy'] = [c for c in dataset_attrs.sortbykey]\nmetadata['entity'] = [c for c in dataset_attrs.entities]\nreturn metadata\ndef _fit_from_datasource(\nself,\nX: DataSource,\nprivacy_level: PrivacyLevel = PrivacyLevel.HIGH_FIDELITY,\ndataset_attrs: Optional[DataSourceAttrs] = None,\ntarget: Optional[str] = None,\nanonymize: Optional[dict] = None,\ncondition_on: Optional[List[str]] = None\n) -> None:\nmetadata = self._metadata_to_payload(\nDataSourceType(X.datatype), X.metadata, dataset_attrs, target)\npayload = {\n'name': self._model.name,\n'dataSourceUID': X.uid,\n'metadata': metadata,\n'extraData': {},\n'privacyLevel': privacy_level.value\n}\nif anonymize is not None:\npayload[\"extraData\"][\"anonymize\"] = anonymize\nif condition_on is not None:\npayload[\"extraData\"][\"condition_on\"] = condition_on\nresponse = self._client.post(\n'/synthesizer/', json=payload, project=self.__project)\ndata: list = response.json()\nself._model, _ = self._model_from_api(X.datatype, data)\nwhile self.status not in [Status.READY, Status.FAILED]:\nself._logger.info('Training the synthesizer...')\nsleep(BACKOFF)\nif self.status == Status.FAILED:\nraise FittingError('Could not train the synthesizer')\n@staticmethod\ndef _model_from_api(datatype: str, data: Dict) -> Tuple[mSynthesizer, Type[\"BaseSynthesizer\"]]:\nfrom ydata.sdk.synthesizers._models.synthesizer_map import TYPE_TO_CLASS\nsynth_cls = TYPE_TO_CLASS.get(SynthesizerType(datatype).value)\ndata['status'] = synth_cls._resolve_api_status(data['status'])\ndata = filter_dict(mSynthesizer, data)\nreturn mSynthesizer(**data), synth_cls\n@abstractmethod\ndef sample(self) -> pdDataFrame:\n\"\"\"Abstract method to sample from a synthesizer.\"\"\"\ndef _sample(self, payload: Dict) -> pdDataFrame:\n\"\"\"Sample from a synthesizer.\n        Arguments:\n            payload (dict): payload configuring the sample request\n        Returns:\n            pandas `DataFrame`\n        \"\"\"\nresponse = self._client.post(\nf\"/synthesizer/{self.uid}/sample\", json=payload, project=self.__project)\ndata: Dict = response.json()\nsample_uid = data.get('uid')\nsample_status = None\nwhile sample_status not in ['finished', 'failed']:\nself._logger.info('Sampling from the synthesizer...')\nresponse = self._client.get(\nf'/synthesizer/{self.uid}/history', project=self.__project)\nhistory: Dict = response.json()\nsample_data = next((s for s in history if s.get('uid') == sample_uid), None)\nsample_status = sample_data.get('status', {}).get('state')\nsleep(BACKOFF)\nresponse = self._client.get_static_file(\nf'/synthesizer/{self.uid}/sample/{sample_uid}/sample.csv', project=self.__project)\ndata = StringIO(response.content.decode())\nreturn read_csv(data)\n@property\ndef uid(self) -> UID:\n\"\"\"Get the status of a synthesizer instance.\n        Returns:\n            Synthesizer status\n        \"\"\"\nif not self._is_initialized():\nreturn Status.NOT_INITIALIZED\nreturn self._model.uid\n@property\ndef status(self) -> Status:\n\"\"\"Get the status of a synthesizer instance.\n        Returns:\n            Synthesizer status\n        \"\"\"\nif not self._is_initialized():\nreturn Status.NOT_INITIALIZED\ntry:\nself = self.get(self._model.uid, self._client)\nreturn self._model.status\nexcept Exception:  # noqa: PIE786\nreturn Status.UNKNOWN\ndef get(self):\nassert self._is_initialized() and self._model.uid, InputError(\n\"Please provide the synthesizer `uid`\")\nresponse = self._client.get(f'/synthesizer/{self.uid}', project=self.__project)\ndata = filter_dict(mSynthesizer, response.json())\nself._model = mSynthesizer(**data)\nreturn self\n@staticmethod\n@init_client\ndef list(client: Optional[Client] = None) -> SynthesizersList:\n\"\"\"List the synthesizer instances.\n        Arguments:\n            client (Client): (optional) Client to connect to the backend\n        Returns:\n            List of synthesizers\n        \"\"\"\ndef __process_data(data: list) -> list:\nto_del = ['metadata', 'report', 'mode']\nfor e in data:\nfor k in to_del:\ne.pop(k, None)\nreturn data\nresponse = client.get('/synthesizer')\ndata: list = response.json()\ndata = __process_data(data)\nreturn SynthesizersList(data)\ndef _is_initialized(self) -> bool:\n\"\"\"Determine if a synthesizer is instanciated or not.\n        Returns:\n            True if the synthesizer is instanciated\n        \"\"\"\nreturn self._model is not None\n@staticmethod\ndef _resolve_api_status(api_status: Dict) -> Status:\n\"\"\"Determine the status of the Synthesizer.\n        The status of the synthesizer instance is determined by the state of\n        its different components.\n        Arguments:\n            api_status (dict): json from the endpoint GET /synthesizer\n        Returns:\n            Synthesizer Status\n        \"\"\"\nstatus = Status(api_status.get('state', Status.UNKNOWN.name))\nif status == Status.PREPARE:\nif PrepareState(api_status.get('prepare', {}).get(\n'state', PrepareState.UNKNOWN.name)) == PrepareState.FAILED:\nreturn Status.FAILED\nelif status == Status.TRAIN:\nif TrainingState(api_status.get('training', {}).get(\n'state', TrainingState.UNKNOWN.name)) == TrainingState.FAILED:\nreturn Status.FAILED\nelif status == Status.REPORT:\nreturn Status.READY\nreturn status\n
"},{"location":"sdk/reference/api/synthesizers/base/#ydata.sdk.synthesizers.synthesizer.BaseSynthesizer.status","title":"status: Status property","text":"

Get the status of a synthesizer instance.

Returns:

Type Description Status

Synthesizer status

"},{"location":"sdk/reference/api/synthesizers/base/#ydata.sdk.synthesizers.synthesizer.BaseSynthesizer.uid","title":"uid: UID property","text":"

Get the status of a synthesizer instance.

Returns:

Type Description UID

Synthesizer status

"},{"location":"sdk/reference/api/synthesizers/base/#ydata.sdk.synthesizers.synthesizer.BaseSynthesizer.fit","title":"fit(X, privacy_level=PrivacyLevel.HIGH_FIDELITY, datatype=None, sortbykey=None, entities=None, generate_cols=None, exclude_cols=None, dtypes=None, target=None, anonymize=None, condition_on=None)","text":"

Fit the synthesizer.

The synthesizer accepts as training dataset either a pandas DataFrame directly or a YData DataSource. When the training dataset is a pandas DataFrame, the argument datatype is required as it cannot be deduced.

The argumentsortbykey is mandatory for TimeSeries.

By default, if generate_cols or exclude_cols are not specified, all columns are generated by the synthesizer. The argument exclude_cols has precedence over generate_cols, i.e. a column col will not be generated if it is in both list.

Parameters:

Name Type Description Default X Union[DataSource, DataFrame]

Training dataset

required privacy_level PrivacyLevel

Synthesizer privacy level (defaults to high fidelity)

HIGH_FIDELITY datatype Optional[Union[DataSourceType, str]]

(optional) Dataset datatype - required if X is a pandas.DataFrame

None sortbykey Union[str, List[str]]

(optional) column(s) to use to sort timeseries datasets

None entities Union[str, List[str]]

(optional) columns representing entities ID

None generate_cols List[str]

(optional) columns that should be synthesized

None exclude_cols List[str]

(optional) columns that should not be synthesized

None dtypes Dict[str, Union[str, DataType]]

(optional) datatype mapping that will overwrite the datasource metadata column datatypes

None target Optional[str]

(optional) Target for the dataset

None name Optional[str]

(optional) Synthesizer instance name

required anonymize Optional[str]

(optional) fields to anonymize and the anonymization strategy

None condition_on Optional[List[str]]

(Optional[List[str]]): (optional) list of features to condition upon

None Source code in ydata/sdk/synthesizers/synthesizer.py
def fit(self, X: Union[DataSource, pdDataFrame],\nprivacy_level: PrivacyLevel = PrivacyLevel.HIGH_FIDELITY,\ndatatype: Optional[Union[DataSourceType, str]] = None,\nsortbykey: Optional[Union[str, List[str]]] = None,\nentities: Optional[Union[str, List[str]]] = None,\ngenerate_cols: Optional[List[str]] = None,\nexclude_cols: Optional[List[str]] = None,\ndtypes: Optional[Dict[str, Union[str, DataType]]] = None,\ntarget: Optional[str] = None,\nanonymize: Optional[dict] = None,\ncondition_on: Optional[List[str]] = None) -> None:\n\"\"\"Fit the synthesizer.\n    The synthesizer accepts as training dataset either a pandas [`DataFrame`][pandas.DataFrame] directly or a YData [`DataSource`][ydata.sdk.datasources.DataSource].\n    When the training dataset is a pandas [`DataFrame`][pandas.DataFrame], the argument `datatype` is required as it cannot be deduced.\n    The argument`sortbykey` is mandatory for [`TimeSeries`][ydata.sdk.datasources.DataSourceType.TIMESERIES].\n    By default, if `generate_cols` or `exclude_cols` are not specified, all columns are generated by the synthesizer.\n    The argument `exclude_cols` has precedence over `generate_cols`, i.e. a column `col` will not be generated if it is in both list.\n    Arguments:\n        X (Union[DataSource, pandas.DataFrame]): Training dataset\n        privacy_level (PrivacyLevel): Synthesizer privacy level (defaults to high fidelity)\n        datatype (Optional[Union[DataSourceType, str]]): (optional) Dataset datatype - required if `X` is a [`pandas.DataFrame`][pandas.DataFrame]\n        sortbykey (Union[str, List[str]]): (optional) column(s) to use to sort timeseries datasets\n        entities (Union[str, List[str]]): (optional) columns representing entities ID\n        generate_cols (List[str]): (optional) columns that should be synthesized\n        exclude_cols (List[str]): (optional) columns that should not be synthesized\n        dtypes (Dict[str, Union[str, DataType]]): (optional) datatype mapping that will overwrite the datasource metadata column datatypes\n        target (Optional[str]): (optional) Target for the dataset\n        name (Optional[str]): (optional) Synthesizer instance name\n        anonymize (Optional[str]): (optional) fields to anonymize and the anonymization strategy\n        condition_on: (Optional[List[str]]): (optional) list of features to condition upon\n    \"\"\"\nif self._is_initialized():\nraise AlreadyFittedError()\n_datatype = DataSourceType(datatype) if isinstance(\nX, pdDataFrame) else DataSourceType(X.datatype)\ndataset_attrs = self._init_datasource_attributes(\nsortbykey, entities, generate_cols, exclude_cols, dtypes)\nself._validate_datasource_attributes(X, dataset_attrs, _datatype, target)\n# If the training data is a pandas dataframe, we first need to create a data source and then the instance\nif isinstance(X, pdDataFrame):\nif X.empty:\nraise EmptyDataError(\"The DataFrame is empty\")\n_X = LocalDataSource(source=X, datatype=_datatype, client=self._client)\nelse:\nif datatype != _datatype:\nwarn(\"When the training data is a DataSource, the argument `datatype` is ignored.\",\nDataSourceTypeWarning)\n_X = X\nif _X.status != dsStatus.AVAILABLE:\nraise DataSourceNotAvailableError(\nf\"The datasource '{_X.uid}' is not available (status = {_X.status.value})\")\nif isinstance(dataset_attrs, dict):\ndataset_attrs = DataSourceAttrs(**dataset_attrs)\nself._fit_from_datasource(\nX=_X, dataset_attrs=dataset_attrs, target=target,\nanonymize=anonymize, privacy_level=privacy_level, condition_on=condition_on)\n
"},{"location":"sdk/reference/api/synthesizers/base/#ydata.sdk.synthesizers.synthesizer.BaseSynthesizer.list","title":"list(client=None) staticmethod","text":"

List the synthesizer instances.

Parameters:

Name Type Description Default client Client

(optional) Client to connect to the backend

None

Returns:

Type Description SynthesizersList

List of synthesizers

Source code in ydata/sdk/synthesizers/synthesizer.py
@staticmethod\n@init_client\ndef list(client: Optional[Client] = None) -> SynthesizersList:\n\"\"\"List the synthesizer instances.\n    Arguments:\n        client (Client): (optional) Client to connect to the backend\n    Returns:\n        List of synthesizers\n    \"\"\"\ndef __process_data(data: list) -> list:\nto_del = ['metadata', 'report', 'mode']\nfor e in data:\nfor k in to_del:\ne.pop(k, None)\nreturn data\nresponse = client.get('/synthesizer')\ndata: list = response.json()\ndata = __process_data(data)\nreturn SynthesizersList(data)\n
"},{"location":"sdk/reference/api/synthesizers/base/#ydata.sdk.synthesizers.synthesizer.BaseSynthesizer.sample","title":"sample() abstractmethod","text":"

Abstract method to sample from a synthesizer.

Source code in ydata/sdk/synthesizers/synthesizer.py
@abstractmethod\ndef sample(self) -> pdDataFrame:\n\"\"\"Abstract method to sample from a synthesizer.\"\"\"\n
"},{"location":"sdk/reference/api/synthesizers/base/#privacylevel","title":"PrivacyLevel","text":"

Bases: StringEnum

Privacy level exposed to the end-user.

"},{"location":"sdk/reference/api/synthesizers/base/#ydata.sdk.synthesizers.PrivacyLevel.BALANCED_PRIVACY_FIDELITY","title":"BALANCED_PRIVACY_FIDELITY = 'BALANCED_PRIVACY_FIDELITY' class-attribute instance-attribute","text":"

Balanced privacy/fidelity

"},{"location":"sdk/reference/api/synthesizers/base/#ydata.sdk.synthesizers.PrivacyLevel.HIGH_FIDELITY","title":"HIGH_FIDELITY = 'HIGH_FIDELITY' class-attribute instance-attribute","text":"

High fidelity

"},{"location":"sdk/reference/api/synthesizers/base/#ydata.sdk.synthesizers.PrivacyLevel.HIGH_PRIVACY","title":"HIGH_PRIVACY = 'HIGH_PRIVACY' class-attribute instance-attribute","text":"

High privacy

"},{"location":"sdk/reference/api/synthesizers/regular/","title":"Regular","text":"

Bases: BaseSynthesizer

Source code in ydata/sdk/synthesizers/regular.py
class RegularSynthesizer(BaseSynthesizer):\ndef sample(self, n_samples: int = 1, condition_on: Optional[dict] = None) -> pdDataFrame:\n\"\"\"Sample from a [`RegularSynthesizer`][ydata.sdk.synthesizers.RegularSynthesizer]\n        instance.\n        Arguments:\n            n_samples (int): number of rows in the sample\n            condition_on: (Optional[dict]): (optional) conditional sampling parameters\n        Returns:\n            synthetic data\n        \"\"\"\nif n_samples < 1:\nraise InputError(\"Parameter 'n_samples' must be greater than 0\")\npayload = {\"numberOfRecords\": n_samples}\nif condition_on is not None:\npayload[\"extraData\"] = {\n\"condition_on\": condition_on\n}\nreturn self._sample(payload=payload)\ndef fit(self, X: Union[DataSource, pdDataFrame],\nprivacy_level: PrivacyLevel = PrivacyLevel.HIGH_FIDELITY,\nentities: Optional[Union[str, List[str]]] = None,\ngenerate_cols: Optional[List[str]] = None,\nexclude_cols: Optional[List[str]] = None,\ndtypes: Optional[Dict[str, Union[str, DataType]]] = None,\ntarget: Optional[str] = None,\nanonymize: Optional[dict] = None,\ncondition_on: Optional[List[str]] = None) -> None:\n\"\"\"Fit the synthesizer.\n        The synthesizer accepts as training dataset either a pandas [`DataFrame`][pandas.DataFrame] directly or a YData [`DataSource`][ydata.sdk.datasources.DataSource].\n        Arguments:\n            X (Union[DataSource, pandas.DataFrame]): Training dataset\n            privacy_level (PrivacyLevel): Synthesizer privacy level (defaults to high fidelity)\n            entities (Union[str, List[str]]): (optional) columns representing entities ID\n            generate_cols (List[str]): (optional) columns that should be synthesized\n            exclude_cols (List[str]): (optional) columns that should not be synthesized\n            dtypes (Dict[str, Union[str, DataType]]): (optional) datatype mapping that will overwrite the datasource metadata column datatypes\n            target (Optional[str]): (optional) Target column\n            name (Optional[str]): (optional) Synthesizer instance name\n            anonymize (Optional[str]): (optional) fields to anonymize and the anonymization strategy\n            condition_on: (Optional[List[str]]): (optional) list of features to condition upon\n        \"\"\"\nBaseSynthesizer.fit(self, X=X, datatype=DataSourceType.TABULAR, entities=entities,\ngenerate_cols=generate_cols, exclude_cols=exclude_cols, dtypes=dtypes,\ntarget=target, anonymize=anonymize, privacy_level=privacy_level,\ncondition_on=condition_on)\ndef __repr__(self):\nif self._model is not None:\nreturn self._model.__repr__()\nelse:\nreturn \"RegularSynthesizer(Not Initialized)\"\n
"},{"location":"sdk/reference/api/synthesizers/regular/#ydata.sdk.synthesizers.regular.RegularSynthesizer.fit","title":"fit(X, privacy_level=PrivacyLevel.HIGH_FIDELITY, entities=None, generate_cols=None, exclude_cols=None, dtypes=None, target=None, anonymize=None, condition_on=None)","text":"

Fit the synthesizer.

The synthesizer accepts as training dataset either a pandas DataFrame directly or a YData DataSource.

Parameters:

Name Type Description Default X Union[DataSource, DataFrame]

Training dataset

required privacy_level PrivacyLevel

Synthesizer privacy level (defaults to high fidelity)

HIGH_FIDELITY entities Union[str, List[str]]

(optional) columns representing entities ID

None generate_cols List[str]

(optional) columns that should be synthesized

None exclude_cols List[str]

(optional) columns that should not be synthesized

None dtypes Dict[str, Union[str, DataType]]

(optional) datatype mapping that will overwrite the datasource metadata column datatypes

None target Optional[str]

(optional) Target column

None name Optional[str]

(optional) Synthesizer instance name

required anonymize Optional[str]

(optional) fields to anonymize and the anonymization strategy

None condition_on Optional[List[str]]

(Optional[List[str]]): (optional) list of features to condition upon

None Source code in ydata/sdk/synthesizers/regular.py
def fit(self, X: Union[DataSource, pdDataFrame],\nprivacy_level: PrivacyLevel = PrivacyLevel.HIGH_FIDELITY,\nentities: Optional[Union[str, List[str]]] = None,\ngenerate_cols: Optional[List[str]] = None,\nexclude_cols: Optional[List[str]] = None,\ndtypes: Optional[Dict[str, Union[str, DataType]]] = None,\ntarget: Optional[str] = None,\nanonymize: Optional[dict] = None,\ncondition_on: Optional[List[str]] = None) -> None:\n\"\"\"Fit the synthesizer.\n    The synthesizer accepts as training dataset either a pandas [`DataFrame`][pandas.DataFrame] directly or a YData [`DataSource`][ydata.sdk.datasources.DataSource].\n    Arguments:\n        X (Union[DataSource, pandas.DataFrame]): Training dataset\n        privacy_level (PrivacyLevel): Synthesizer privacy level (defaults to high fidelity)\n        entities (Union[str, List[str]]): (optional) columns representing entities ID\n        generate_cols (List[str]): (optional) columns that should be synthesized\n        exclude_cols (List[str]): (optional) columns that should not be synthesized\n        dtypes (Dict[str, Union[str, DataType]]): (optional) datatype mapping that will overwrite the datasource metadata column datatypes\n        target (Optional[str]): (optional) Target column\n        name (Optional[str]): (optional) Synthesizer instance name\n        anonymize (Optional[str]): (optional) fields to anonymize and the anonymization strategy\n        condition_on: (Optional[List[str]]): (optional) list of features to condition upon\n    \"\"\"\nBaseSynthesizer.fit(self, X=X, datatype=DataSourceType.TABULAR, entities=entities,\ngenerate_cols=generate_cols, exclude_cols=exclude_cols, dtypes=dtypes,\ntarget=target, anonymize=anonymize, privacy_level=privacy_level,\ncondition_on=condition_on)\n
"},{"location":"sdk/reference/api/synthesizers/regular/#ydata.sdk.synthesizers.regular.RegularSynthesizer.sample","title":"sample(n_samples=1, condition_on=None)","text":"

Sample from a RegularSynthesizer instance.

Parameters:

Name Type Description Default n_samples int

number of rows in the sample

1 condition_on Optional[dict]

(Optional[dict]): (optional) conditional sampling parameters

None

Returns:

Type Description DataFrame

synthetic data

Source code in ydata/sdk/synthesizers/regular.py
def sample(self, n_samples: int = 1, condition_on: Optional[dict] = None) -> pdDataFrame:\n\"\"\"Sample from a [`RegularSynthesizer`][ydata.sdk.synthesizers.RegularSynthesizer]\n    instance.\n    Arguments:\n        n_samples (int): number of rows in the sample\n        condition_on: (Optional[dict]): (optional) conditional sampling parameters\n    Returns:\n        synthetic data\n    \"\"\"\nif n_samples < 1:\nraise InputError(\"Parameter 'n_samples' must be greater than 0\")\npayload = {\"numberOfRecords\": n_samples}\nif condition_on is not None:\npayload[\"extraData\"] = {\n\"condition_on\": condition_on\n}\nreturn self._sample(payload=payload)\n
"},{"location":"sdk/reference/api/synthesizers/regular/#privacylevel","title":"PrivacyLevel","text":"

Bases: StringEnum

Privacy level exposed to the end-user.

"},{"location":"sdk/reference/api/synthesizers/regular/#ydata.sdk.synthesizers.PrivacyLevel.BALANCED_PRIVACY_FIDELITY","title":"BALANCED_PRIVACY_FIDELITY = 'BALANCED_PRIVACY_FIDELITY' class-attribute instance-attribute","text":"

Balanced privacy/fidelity

"},{"location":"sdk/reference/api/synthesizers/regular/#ydata.sdk.synthesizers.PrivacyLevel.HIGH_FIDELITY","title":"HIGH_FIDELITY = 'HIGH_FIDELITY' class-attribute instance-attribute","text":"

High fidelity

"},{"location":"sdk/reference/api/synthesizers/regular/#ydata.sdk.synthesizers.PrivacyLevel.HIGH_PRIVACY","title":"HIGH_PRIVACY = 'HIGH_PRIVACY' class-attribute instance-attribute","text":"

High privacy

"},{"location":"sdk/reference/api/synthesizers/timeseries/","title":"TimeSeries","text":"

Bases: BaseSynthesizer

Source code in ydata/sdk/synthesizers/timeseries.py
class TimeSeriesSynthesizer(BaseSynthesizer):\ndef sample(self, n_entities: int, condition_on: Optional[dict] = None) -> pdDataFrame:\n\"\"\"Sample from a [`TimeSeriesSynthesizer`][ydata.sdk.synthesizers.TimeSeriesSynthesizer] instance.\n        If a training dataset was not using any `entity` column, the Synthesizer assumes a single entity.\n        A [`TimeSeriesSynthesizer`][ydata.sdk.synthesizers.TimeSeriesSynthesizer] always sample the full trajectory of its entities.\n        Arguments:\n            n_entities (int): number of entities to sample\n            condition_on: (Optional[dict]): (optional) conditional sampling parameters\n        Returns:\n            synthetic data\n        \"\"\"\nif n_entities is not None and n_entities < 1:\nraise InputError(\"Parameter 'n_entities' must be greater than 0\")\npayload = {\"numberOfRecords\": n_entities}\nif condition_on is not None:\npayload[\"extraData\"] = {\n\"condition_on\": condition_on\n}\nreturn self._sample(payload=payload)\ndef fit(self, X: Union[DataSource, pdDataFrame],\nsortbykey: Optional[Union[str, List[str]]],\nprivacy_level: PrivacyLevel = PrivacyLevel.HIGH_FIDELITY,\nentities: Optional[Union[str, List[str]]] = None,\ngenerate_cols: Optional[List[str]] = None,\nexclude_cols: Optional[List[str]] = None,\ndtypes: Optional[Dict[str, Union[str, DataType]]] = None,\ntarget: Optional[str] = None,\nanonymize: Optional[dict] = None,\ncondition_on: Optional[List[str]] = None) -> None:\n\"\"\"Fit the synthesizer.\n        The synthesizer accepts as training dataset either a pandas [`DataFrame`][pandas.DataFrame] directly or a YData [`DataSource`][ydata.sdk.datasources.DataSource].\n        Arguments:\n            X (Union[DataSource, pandas.DataFrame]): Training dataset\n            sortbykey (Union[str, List[str]]): column(s) to use to sort timeseries datasets\n            privacy_level (PrivacyLevel): Synthesizer privacy level (defaults to high fidelity)\n            entities (Union[str, List[str]]): (optional) columns representing entities ID\n            generate_cols (List[str]): (optional) columns that should be synthesized\n            exclude_cols (List[str]): (optional) columns that should not be synthesized\n            dtypes (Dict[str, Union[str, DataType]]): (optional) datatype mapping that will overwrite the datasource metadata column datatypes\n            target (Optional[str]): (optional) Metadata associated to the datasource\n            name (Optional[str]): (optional) Synthesizer instance name\n            anonymize (Optional[str]): (optional) fields to anonymize and the anonymization strategy\n            condition_on: (Optional[List[str]]): (optional) list of features to condition upon\n        \"\"\"\nBaseSynthesizer.fit(self, X=X, datatype=DataSourceType.TIMESERIES, sortbykey=sortbykey,\nentities=entities, generate_cols=generate_cols, exclude_cols=exclude_cols,\ndtypes=dtypes, target=target, anonymize=anonymize, privacy_level=privacy_level,\ncondition_on=condition_on)\ndef __repr__(self):\nif self._model is not None:\nreturn self._model.__repr__()\nelse:\nreturn \"TimeSeriesSynthesizer(Not Initialized)\"\n
"},{"location":"sdk/reference/api/synthesizers/timeseries/#ydata.sdk.synthesizers.timeseries.TimeSeriesSynthesizer.fit","title":"fit(X, sortbykey, privacy_level=PrivacyLevel.HIGH_FIDELITY, entities=None, generate_cols=None, exclude_cols=None, dtypes=None, target=None, anonymize=None, condition_on=None)","text":"

Fit the synthesizer.

The synthesizer accepts as training dataset either a pandas DataFrame directly or a YData DataSource.

Parameters:

Name Type Description Default X Union[DataSource, DataFrame]

Training dataset

required sortbykey Union[str, List[str]]

column(s) to use to sort timeseries datasets

required privacy_level PrivacyLevel

Synthesizer privacy level (defaults to high fidelity)

HIGH_FIDELITY entities Union[str, List[str]]

(optional) columns representing entities ID

None generate_cols List[str]

(optional) columns that should be synthesized

None exclude_cols List[str]

(optional) columns that should not be synthesized

None dtypes Dict[str, Union[str, DataType]]

(optional) datatype mapping that will overwrite the datasource metadata column datatypes

None target Optional[str]

(optional) Metadata associated to the datasource

None name Optional[str]

(optional) Synthesizer instance name

required anonymize Optional[str]

(optional) fields to anonymize and the anonymization strategy

None condition_on Optional[List[str]]

(Optional[List[str]]): (optional) list of features to condition upon

None Source code in ydata/sdk/synthesizers/timeseries.py
def fit(self, X: Union[DataSource, pdDataFrame],\nsortbykey: Optional[Union[str, List[str]]],\nprivacy_level: PrivacyLevel = PrivacyLevel.HIGH_FIDELITY,\nentities: Optional[Union[str, List[str]]] = None,\ngenerate_cols: Optional[List[str]] = None,\nexclude_cols: Optional[List[str]] = None,\ndtypes: Optional[Dict[str, Union[str, DataType]]] = None,\ntarget: Optional[str] = None,\nanonymize: Optional[dict] = None,\ncondition_on: Optional[List[str]] = None) -> None:\n\"\"\"Fit the synthesizer.\n    The synthesizer accepts as training dataset either a pandas [`DataFrame`][pandas.DataFrame] directly or a YData [`DataSource`][ydata.sdk.datasources.DataSource].\n    Arguments:\n        X (Union[DataSource, pandas.DataFrame]): Training dataset\n        sortbykey (Union[str, List[str]]): column(s) to use to sort timeseries datasets\n        privacy_level (PrivacyLevel): Synthesizer privacy level (defaults to high fidelity)\n        entities (Union[str, List[str]]): (optional) columns representing entities ID\n        generate_cols (List[str]): (optional) columns that should be synthesized\n        exclude_cols (List[str]): (optional) columns that should not be synthesized\n        dtypes (Dict[str, Union[str, DataType]]): (optional) datatype mapping that will overwrite the datasource metadata column datatypes\n        target (Optional[str]): (optional) Metadata associated to the datasource\n        name (Optional[str]): (optional) Synthesizer instance name\n        anonymize (Optional[str]): (optional) fields to anonymize and the anonymization strategy\n        condition_on: (Optional[List[str]]): (optional) list of features to condition upon\n    \"\"\"\nBaseSynthesizer.fit(self, X=X, datatype=DataSourceType.TIMESERIES, sortbykey=sortbykey,\nentities=entities, generate_cols=generate_cols, exclude_cols=exclude_cols,\ndtypes=dtypes, target=target, anonymize=anonymize, privacy_level=privacy_level,\ncondition_on=condition_on)\n
"},{"location":"sdk/reference/api/synthesizers/timeseries/#ydata.sdk.synthesizers.timeseries.TimeSeriesSynthesizer.sample","title":"sample(n_entities, condition_on=None)","text":"

Sample from a TimeSeriesSynthesizer instance.

If a training dataset was not using any entity column, the Synthesizer assumes a single entity. A TimeSeriesSynthesizer always sample the full trajectory of its entities.

Parameters:

Name Type Description Default n_entities int

number of entities to sample

required condition_on Optional[dict]

(Optional[dict]): (optional) conditional sampling parameters

None

Returns:

Type Description DataFrame

synthetic data

Source code in ydata/sdk/synthesizers/timeseries.py
def sample(self, n_entities: int, condition_on: Optional[dict] = None) -> pdDataFrame:\n\"\"\"Sample from a [`TimeSeriesSynthesizer`][ydata.sdk.synthesizers.TimeSeriesSynthesizer] instance.\n    If a training dataset was not using any `entity` column, the Synthesizer assumes a single entity.\n    A [`TimeSeriesSynthesizer`][ydata.sdk.synthesizers.TimeSeriesSynthesizer] always sample the full trajectory of its entities.\n    Arguments:\n        n_entities (int): number of entities to sample\n        condition_on: (Optional[dict]): (optional) conditional sampling parameters\n    Returns:\n        synthetic data\n    \"\"\"\nif n_entities is not None and n_entities < 1:\nraise InputError(\"Parameter 'n_entities' must be greater than 0\")\npayload = {\"numberOfRecords\": n_entities}\nif condition_on is not None:\npayload[\"extraData\"] = {\n\"condition_on\": condition_on\n}\nreturn self._sample(payload=payload)\n
"},{"location":"sdk/reference/api/synthesizers/timeseries/#privacylevel","title":"PrivacyLevel","text":"

Bases: StringEnum

Privacy level exposed to the end-user.

"},{"location":"sdk/reference/api/synthesizers/timeseries/#ydata.sdk.synthesizers.PrivacyLevel.BALANCED_PRIVACY_FIDELITY","title":"BALANCED_PRIVACY_FIDELITY = 'BALANCED_PRIVACY_FIDELITY' class-attribute instance-attribute","text":"

Balanced privacy/fidelity

"},{"location":"sdk/reference/api/synthesizers/timeseries/#ydata.sdk.synthesizers.PrivacyLevel.HIGH_FIDELITY","title":"HIGH_FIDELITY = 'HIGH_FIDELITY' class-attribute instance-attribute","text":"

High fidelity

"},{"location":"sdk/reference/api/synthesizers/timeseries/#ydata.sdk.synthesizers.PrivacyLevel.HIGH_PRIVACY","title":"HIGH_PRIVACY = 'HIGH_PRIVACY' class-attribute instance-attribute","text":"

High privacy

"},{"location":"support/help-troubleshooting/","title":"Help & Troubleshooting","text":""}]} \ No newline at end of file diff --git a/0.7/sitemap.xml b/0.7/sitemap.xml new file mode 100644 index 00000000..0f8724ef --- /dev/null +++ b/0.7/sitemap.xml @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/0.7/sitemap.xml.gz b/0.7/sitemap.xml.gz new file mode 100644 index 0000000000000000000000000000000000000000..131d4f117ead9cdfb9a66405dc67dd58bf9f95a4 GIT binary patch literal 127 zcmV-_0D%7=iwFoggM(!P|8r?{Wo=<_E_iKh04<9_3V)_WXo8&M?ytk3HC}0~zlG)Vu ::before { + content: attr(data-md-annotation-id); +} +.md-typeset :focus-within > .md-annotation__index > ::before { + transform: none; +} + +@media { + .md-button--ydata { + --md-primary-fg-color: #E32212; + --md-primary-bg-color: #E32212; + } +} + +:root { + /* Primary color shades */ + --md-primary-fg-color: #040404; + --md-primary-fg-color--light: #040404; + --md-primary-fg-color--dark: #040404; + --md-primary-bg-color: hsla(0, 0%, 100%, 1); + --md-primary-bg-color--light: hsla(0, 0%, 100%, 0.7); + --md-text-link-color: #E32212; + + /* Accent color shades */ + --md-accent-fg-color: #E32212; + --md-accent-fg-color--transparent: hsla(189, 100%, 37%, 0.1); + --md-accent-bg-color: hsla(0, 0%, 100%, 1); + --md-accent-bg-color--light: hsla(0, 0%, 100%, 0.7); + } + + :root > * { + /* Code block color shades */ + --md-code-bg-color: hsla(0, 0%, 96%, 1); + --md-code-fg-color: hsla(200, 18%, 26%, 1); + + /* Footer */ + --md-footer-bg-color: #040404; + --md-footer-bg-color--dark: hsla(0, 0%, 0%, 0.32); + --md-footer-fg-color: hsla(0, 0%, 100%, 1); + --md-footer-fg-color--light: hsla(0, 0%, 100%, 0.7); + --md-footer-fg-color--lighter: hsla(0, 0%, 100%, 0.3); + } + +.youtube { + color: #EE0F0F; +} diff --git a/0.7/support/help-troubleshooting/index.html b/0.7/support/help-troubleshooting/index.html new file mode 100644 index 00000000..d3e62efe --- /dev/null +++ b/0.7/support/help-troubleshooting/index.html @@ -0,0 +1,1197 @@ + + + + + + + + + + + + + + + + + + Help & Troubleshooting - YData Fabric + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ + + + + + +
+ + +
+ +
+ + + + + + + + + +
+
+ + + +
+
+
+ + + + + + + + +
+
+
+ + + + +
+
+ + + + +

Help & Troubleshooting

+ + + + + + +
+
+ + + + +
+ + + +
+ +
+ + + + +
+ +
+
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/latest/404.html b/latest/404.html index 494e5c67..bc6a307c 100644 --- a/latest/404.html +++ b/latest/404.html @@ -4,13 +4,13 @@ Redirecting - Redirecting to ../0.6/404.html... + Redirecting to ../0.7/404.html... \ No newline at end of file diff --git a/latest/examples/synthesize_tabular_data/index.html b/latest/examples/synthesize_tabular_data/index.html index 5efb4868..542c7af1 100644 --- a/latest/examples/synthesize_tabular_data/index.html +++ b/latest/examples/synthesize_tabular_data/index.html @@ -4,13 +4,13 @@ Redirecting - Redirecting to ../../../0.6/examples/synthesize_tabular_data/... + Redirecting to ../../../0.7/examples/synthesize_tabular_data/... \ No newline at end of file diff --git a/latest/examples/synthesize_timeseries_data/index.html b/latest/examples/synthesize_timeseries_data/index.html index 2a27c0ee..05339cfc 100644 --- a/latest/examples/synthesize_timeseries_data/index.html +++ b/latest/examples/synthesize_timeseries_data/index.html @@ -4,13 +4,13 @@ Redirecting - Redirecting to ../../../0.6/examples/synthesize_timeseries_data/... + Redirecting to ../../../0.7/examples/synthesize_timeseries_data/... \ No newline at end of file diff --git a/latest/examples/synthesize_with_anonymization/index.html b/latest/examples/synthesize_with_anonymization/index.html index 6a622757..dc74e31a 100644 --- a/latest/examples/synthesize_with_anonymization/index.html +++ b/latest/examples/synthesize_with_anonymization/index.html @@ -4,13 +4,13 @@ Redirecting - Redirecting to ../../../0.6/examples/synthesize_with_anonymization/... + Redirecting to ../../../0.7/examples/synthesize_with_anonymization/... \ No newline at end of file diff --git a/latest/examples/synthesize_with_conditional_sampling/index.html b/latest/examples/synthesize_with_conditional_sampling/index.html index 17b34286..dd76836d 100644 --- a/latest/examples/synthesize_with_conditional_sampling/index.html +++ b/latest/examples/synthesize_with_conditional_sampling/index.html @@ -4,13 +4,13 @@ Redirecting - Redirecting to ../../../0.6/examples/synthesize_with_conditional_sampling/... + Redirecting to ../../../0.7/examples/synthesize_with_conditional_sampling/... \ No newline at end of file diff --git a/latest/examples/synthesize_with_privacy_control/index.html b/latest/examples/synthesize_with_privacy_control/index.html index 190e429b..b4a2e603 100644 --- a/latest/examples/synthesize_with_privacy_control/index.html +++ b/latest/examples/synthesize_with_privacy_control/index.html @@ -4,13 +4,13 @@ Redirecting - Redirecting to ../../../0.6/examples/synthesize_with_privacy_control/... + Redirecting to ../../../0.7/examples/synthesize_with_privacy_control/... \ No newline at end of file diff --git a/latest/get-started/create_lab/index.html b/latest/get-started/create_lab/index.html index c14db7c2..81819687 100644 --- a/latest/get-started/create_lab/index.html +++ b/latest/get-started/create_lab/index.html @@ -4,13 +4,13 @@ Redirecting - Redirecting to ../../../0.6/get-started/create_lab/... + Redirecting to ../../../0.7/get-started/create_lab/... \ No newline at end of file diff --git a/latest/get-started/create_pipeline/index.html b/latest/get-started/create_pipeline/index.html index fcb352f3..e4371c38 100644 --- a/latest/get-started/create_pipeline/index.html +++ b/latest/get-started/create_pipeline/index.html @@ -4,13 +4,13 @@ Redirecting - Redirecting to ../../../0.6/get-started/create_pipeline/... + Redirecting to ../../../0.7/get-started/create_pipeline/... \ No newline at end of file diff --git a/latest/get-started/create_syntheticdata_generator/index.html b/latest/get-started/create_syntheticdata_generator/index.html index f5b1aa10..b613c429 100644 --- a/latest/get-started/create_syntheticdata_generator/index.html +++ b/latest/get-started/create_syntheticdata_generator/index.html @@ -4,13 +4,13 @@ Redirecting - Redirecting to ../../../0.6/get-started/create_syntheticdata_generator/... + Redirecting to ../../../0.7/get-started/create_syntheticdata_generator/... \ No newline at end of file diff --git a/latest/get-started/fabric_community/index.html b/latest/get-started/fabric_community/index.html index 71303a8e..51dfa1ad 100644 --- a/latest/get-started/fabric_community/index.html +++ b/latest/get-started/fabric_community/index.html @@ -4,13 +4,13 @@ Redirecting - Redirecting to ../../../0.6/get-started/fabric_community/... + Redirecting to ../../../0.7/get-started/fabric_community/... \ No newline at end of file diff --git a/latest/get-started/index.html b/latest/get-started/index.html index b7fd7d1a..b4bfb7c7 100644 --- a/latest/get-started/index.html +++ b/latest/get-started/index.html @@ -4,13 +4,13 @@ Redirecting - Redirecting to ../../0.6/get-started/... + Redirecting to ../../0.7/get-started/... \ No newline at end of file diff --git a/latest/get-started/upload_csv/index.html b/latest/get-started/upload_csv/index.html index 1e726e38..4feee32e 100644 --- a/latest/get-started/upload_csv/index.html +++ b/latest/get-started/upload_csv/index.html @@ -4,13 +4,13 @@ Redirecting - Redirecting to ../../../0.6/get-started/upload_csv/... + Redirecting to ../../../0.7/get-started/upload_csv/... \ No newline at end of file diff --git a/latest/index.html b/latest/index.html index 9973c4a5..76818139 100644 --- a/latest/index.html +++ b/latest/index.html @@ -4,13 +4,13 @@ Redirecting - Redirecting to ../0.6/... + Redirecting to ../0.7/... \ No newline at end of file diff --git a/latest/sdk/index.html b/latest/sdk/index.html index 4bd4f16c..7f6e7964 100644 --- a/latest/sdk/index.html +++ b/latest/sdk/index.html @@ -4,13 +4,13 @@ Redirecting - Redirecting to ../../0.6/sdk/... + Redirecting to ../../0.7/sdk/... \ No newline at end of file diff --git a/latest/sdk/installation/index.html b/latest/sdk/installation/index.html index 9d0b46eb..f87ec5ce 100644 --- a/latest/sdk/installation/index.html +++ b/latest/sdk/installation/index.html @@ -4,13 +4,13 @@ Redirecting - Redirecting to ../../../0.6/sdk/installation/... + Redirecting to ../../../0.7/sdk/installation/... \ No newline at end of file diff --git a/latest/sdk/modules/connectors/index.html b/latest/sdk/modules/connectors/index.html index 9b90645c..960e9bc0 100644 --- a/latest/sdk/modules/connectors/index.html +++ b/latest/sdk/modules/connectors/index.html @@ -4,13 +4,13 @@ Redirecting - Redirecting to ../../../../0.6/sdk/modules/connectors/... + Redirecting to ../../../../0.7/sdk/modules/connectors/... \ No newline at end of file diff --git a/latest/sdk/modules/synthetic_data/index.html b/latest/sdk/modules/synthetic_data/index.html index 78b74b89..28e9b81f 100644 --- a/latest/sdk/modules/synthetic_data/index.html +++ b/latest/sdk/modules/synthetic_data/index.html @@ -4,13 +4,13 @@ Redirecting - Redirecting to ../../../../0.6/sdk/modules/synthetic_data/... + Redirecting to ../../../../0.7/sdk/modules/synthetic_data/... \ No newline at end of file diff --git a/latest/sdk/quickstart/index.html b/latest/sdk/quickstart/index.html index edddff6e..890bf096 100644 --- a/latest/sdk/quickstart/index.html +++ b/latest/sdk/quickstart/index.html @@ -4,13 +4,13 @@ Redirecting - Redirecting to ../../../0.6/sdk/quickstart/... + Redirecting to ../../../0.7/sdk/quickstart/... \ No newline at end of file diff --git a/latest/sdk/reference/api/common/client/index.html b/latest/sdk/reference/api/common/client/index.html index 51892f21..52ede298 100644 --- a/latest/sdk/reference/api/common/client/index.html +++ b/latest/sdk/reference/api/common/client/index.html @@ -4,13 +4,13 @@ Redirecting - Redirecting to ../../../../../../0.6/sdk/reference/api/common/client/... + Redirecting to ../../../../../../0.7/sdk/reference/api/common/client/... \ No newline at end of file diff --git a/latest/sdk/reference/api/common/types/index.html b/latest/sdk/reference/api/common/types/index.html index 3bbb6a86..06ae9342 100644 --- a/latest/sdk/reference/api/common/types/index.html +++ b/latest/sdk/reference/api/common/types/index.html @@ -4,13 +4,13 @@ Redirecting - Redirecting to ../../../../../../0.6/sdk/reference/api/common/types/... + Redirecting to ../../../../../../0.7/sdk/reference/api/common/types/... \ No newline at end of file diff --git a/latest/sdk/reference/api/connectors/connector/index.html b/latest/sdk/reference/api/connectors/connector/index.html index 20f799f1..60917faa 100644 --- a/latest/sdk/reference/api/connectors/connector/index.html +++ b/latest/sdk/reference/api/connectors/connector/index.html @@ -4,13 +4,13 @@ Redirecting - Redirecting to ../../../../../../0.6/sdk/reference/api/connectors/connector/... + Redirecting to ../../../../../../0.7/sdk/reference/api/connectors/connector/... \ No newline at end of file diff --git a/latest/sdk/reference/api/datasources/datasource/index.html b/latest/sdk/reference/api/datasources/datasource/index.html index adbceaf3..263680b9 100644 --- a/latest/sdk/reference/api/datasources/datasource/index.html +++ b/latest/sdk/reference/api/datasources/datasource/index.html @@ -4,13 +4,13 @@ Redirecting - Redirecting to ../../../../../../0.6/sdk/reference/api/datasources/datasource/... + Redirecting to ../../../../../../0.7/sdk/reference/api/datasources/datasource/... \ No newline at end of file diff --git a/latest/sdk/reference/api/datasources/metadata/index.html b/latest/sdk/reference/api/datasources/metadata/index.html index dc10fd1a..360d3117 100644 --- a/latest/sdk/reference/api/datasources/metadata/index.html +++ b/latest/sdk/reference/api/datasources/metadata/index.html @@ -4,13 +4,13 @@ Redirecting - Redirecting to ../../../../../../0.6/sdk/reference/api/datasources/metadata/... + Redirecting to ../../../../../../0.7/sdk/reference/api/datasources/metadata/... \ No newline at end of file diff --git a/latest/sdk/reference/api/index.html b/latest/sdk/reference/api/index.html index b01c556a..f77edcfb 100644 --- a/latest/sdk/reference/api/index.html +++ b/latest/sdk/reference/api/index.html @@ -4,13 +4,13 @@ Redirecting - Redirecting to ../../../../0.6/sdk/reference/api/... + Redirecting to ../../../../0.7/sdk/reference/api/... \ No newline at end of file diff --git a/latest/sdk/reference/api/synthesizers/base/index.html b/latest/sdk/reference/api/synthesizers/base/index.html index 0879564b..841b4ff6 100644 --- a/latest/sdk/reference/api/synthesizers/base/index.html +++ b/latest/sdk/reference/api/synthesizers/base/index.html @@ -4,13 +4,13 @@ Redirecting - Redirecting to ../../../../../../0.6/sdk/reference/api/synthesizers/base/... + Redirecting to ../../../../../../0.7/sdk/reference/api/synthesizers/base/... \ No newline at end of file diff --git a/latest/sdk/reference/api/synthesizers/regular/index.html b/latest/sdk/reference/api/synthesizers/regular/index.html index 4e2f0624..dfae2258 100644 --- a/latest/sdk/reference/api/synthesizers/regular/index.html +++ b/latest/sdk/reference/api/synthesizers/regular/index.html @@ -4,13 +4,13 @@ Redirecting - Redirecting to ../../../../../../0.6/sdk/reference/api/synthesizers/regular/... + Redirecting to ../../../../../../0.7/sdk/reference/api/synthesizers/regular/... \ No newline at end of file diff --git a/latest/sdk/reference/api/synthesizers/timeseries/index.html b/latest/sdk/reference/api/synthesizers/timeseries/index.html index 9c93633e..a3a0ba03 100644 --- a/latest/sdk/reference/api/synthesizers/timeseries/index.html +++ b/latest/sdk/reference/api/synthesizers/timeseries/index.html @@ -4,13 +4,13 @@ Redirecting - Redirecting to ../../../../../../0.6/sdk/reference/api/synthesizers/timeseries/... + Redirecting to ../../../../../../0.7/sdk/reference/api/synthesizers/timeseries/... \ No newline at end of file diff --git a/latest/sdk/reference/changelog/index.html b/latest/sdk/reference/changelog/index.html index 807d6251..f116c57d 100644 --- a/latest/sdk/reference/changelog/index.html +++ b/latest/sdk/reference/changelog/index.html @@ -4,13 +4,13 @@ Redirecting - Redirecting to ../../../../0.6/sdk/reference/changelog/... + Redirecting to ../../../../0.7/sdk/reference/changelog/... \ No newline at end of file diff --git a/latest/support/help-troubleshooting/index.html b/latest/support/help-troubleshooting/index.html index ce581267..e4b16d35 100644 --- a/latest/support/help-troubleshooting/index.html +++ b/latest/support/help-troubleshooting/index.html @@ -4,13 +4,13 @@ Redirecting - Redirecting to ../../../0.6/support/help-troubleshooting/... + Redirecting to ../../../0.7/support/help-troubleshooting/... \ No newline at end of file diff --git a/versions.json b/versions.json index b6125945..cec16db4 100644 --- a/versions.json +++ b/versions.json @@ -1 +1 @@ -[{"version": "0.6", "title": "0.6", "aliases": ["latest"]}, {"version": "0.5", "title": "0.5", "aliases": []}, {"version": "0.4", "title": "0.4", "aliases": []}, {"version": "0.3", "title": "0.3", "aliases": []}, {"version": "0.2", "title": "0.2", "aliases": []}, {"version": "0.1", "title": "0.1", "aliases": []}, {"version": "0.0", "title": "0.0", "aliases": []}] \ No newline at end of file +[{"version": "0.7", "title": "0.7", "aliases": ["latest"]}, {"version": "0.6", "title": "0.6", "aliases": []}, {"version": "0.5", "title": "0.5", "aliases": []}, {"version": "0.4", "title": "0.4", "aliases": []}, {"version": "0.3", "title": "0.3", "aliases": []}, {"version": "0.2", "title": "0.2", "aliases": []}, {"version": "0.1", "title": "0.1", "aliases": []}, {"version": "0.0", "title": "0.0", "aliases": []}] \ No newline at end of file