From 6326d27e159df904bdff069b82ea86c17199c23d Mon Sep 17 00:00:00 2001 From: Badr Ouali <32390048+oualib@users.noreply.github.com> Date: Thu, 24 Oct 2024 09:33:33 -0400 Subject: [PATCH] User Guide - Final (#1322) * User Guide - Final * Query Profiler notebook * Create user_guide_performance_qprof_get_qplan_tree.PNG * vdf * big bug * multiple corrections * correcting the ML bug - wrong types * read_csv correction * fix feature engineering notebook * correction --------- Co-authored-by: Umar Farooq Ghumman --- ...guide_performance_qprof_get_qplan_tree.PNG | Bin 0 -> 49557 bytes ...ution_guidelines_code_auto_doc_example.rst | 4 +- .../source/examples_business_base_station.rst | 2 +- docs/source/examples_business_battery.rst | 2 +- docs/source/examples_business_booking.rst | 4 +- docs/source/examples_business_churn.rst | 2 +- .../examples_business_credit_card_fraud.rst | 6 +- docs/source/examples_business_football.rst | 4 +- docs/source/examples_business_insurance.rst | 2 +- docs/source/examples_business_movies.rst | 8 +- .../source/examples_business_smart_meters.rst | 2 +- docs/source/examples_business_spam.rst | 4 +- docs/source/examples_business_spotify.rst | 12 +- docs/source/examples_learn_commodities.rst | 6 +- docs/source/examples_learn_iris.rst | 8 +- docs/source/examples_learn_pokemon.rst | 2 +- docs/source/examples_learn_titanic.rst | 6 +- .../examples_understand_africa_education.rst | 4 +- docs/source/examples_understand_covid19.rst | 10 +- docs/source/user_guide_data_ingestion.rst | 2 +- docs/source/user_guide_data_preparation.rst | 2 +- ...r_guide_data_preparation_decomposition.rst | 6 +- ..._data_preparation_features_engineering.rst | 7 +- .../user_guide_data_preparation_outliers.rst | 4 +- ...ser_guide_full_stack_complex_data_vmap.rst | 224 ++++---- .../user_guide_full_stack_geopandas.rst | 2 +- ...ser_guide_full_stack_linear_regression.rst | 8 +- docs/source/user_guide_full_stack_to_json.rst | 8 +- ...user_guide_full_stack_vdataframe_magic.rst | 6 +- docs/source/user_guide_introduction.rst | 2 +- ...user_guide_introduction_best_practices.rst | 18 +- docs/source/user_guide_introduction_vdf.rst | 514 ++++++++++++++++++ ...er_guide_machine_learning_introduction.rst | 2 +- ..._guide_machine_learning_model_tracking.rst | 2 +- ...ser_guide_machine_learning_time_series.rst | 2 +- docs/source/user_guide_performance.rst | 5 +- docs/source/user_guide_performance_qprof.rst | 506 +++++++++++++++++ docs/source/whats_new_v1_0_2.rst | 2 +- verticapy/__init__.py | 4 +- verticapy/core/parsers/csv.py | 25 +- verticapy/core/vdataframe/_read.py | 13 +- .../machine_learning/vertica/linear_model.py | 12 +- 42 files changed, 1240 insertions(+), 224 deletions(-) create mode 100644 docs/source/_static/website/user_guides/performance/user_guide_performance_qprof_get_qplan_tree.PNG create mode 100644 docs/source/user_guide_introduction_vdf.rst create mode 100644 docs/source/user_guide_performance_qprof.rst diff --git a/docs/source/_static/website/user_guides/performance/user_guide_performance_qprof_get_qplan_tree.PNG b/docs/source/_static/website/user_guides/performance/user_guide_performance_qprof_get_qplan_tree.PNG new file mode 100644 index 0000000000000000000000000000000000000000..20b85ac335d0c5013203e3dac2a44e5bccf0a616 GIT binary patch literal 49557 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 b1AI&ElFv3-Wm|Bi&@Vq`_. - Loading Complex Data --------------------- There are two ways to load a nested data file: -- Load directly using :py:func:`verticapy.read_json`. - In this case, you will need to use an additional parameter - to identify all the data types. The function loads the - data using flex tables and VMaps (Native Vertica MAPS, - which are flexible but not optimally performant). - -- Load using :py:func:`verticapy.read_file`. The function preidcts the complex data structure. +- Load directly using :py:func:`~verticapy.read_json`. In this case, you will need to use an additional parameter to identify all the data types. The function loads the data using flex tables and VMaps (Native Vertica MAPS, which are flexible but not optimally performant). +- Load using :py:func:`~verticapy.read_file`. The function preidcts the complex data structure. Let's try both: .. code-block:: python - import verticapy as vp data = vp.read_json( path + "laliga/2008.json", schema = "public", ingest_local = False, use_complex_dt = True, - genSQL = True ) -Similar to the use of :py:func:`verticapy.read_json` above, -we can use :py:func:`verticapy.read_file` to ingest the complex data directly: - -.. code-block:: python - - data = vp.read_file( - path = path + "laliga/2005.json", - ingest_local = False, - schema = "complex_vmap_test", - ) - data - .. ipython:: python :suppress: from verticapy.datasets import load_laliga data = load_laliga() - res = data + res = data.head(100) html_file = open("/project/data/VerticaPy/docs/figures/ug_fs_complex_data.html", "w") html_file.write(res._repr_html_()) html_file.close() @@ -114,11 +82,34 @@ we can use :py:func:`verticapy.read_file` to ingest the complex data directly: .. raw:: html :file: /project/data/VerticaPy/docs/figures/ug_fs_complex_data.html -We can also use the handy ``genSQL`` parameter to generate -(but not execute) the SQL needed to create the final relation: +Similar to the use of :py:func:`~verticapy.read_json` above, we can use :py:func:`~verticapy.read_file` to ingest the complex data directly: + +.. code-block:: python + + data = vp.read_file( + path = path + "laliga/2005.json", + ingest_local = False, + schema = "complex_vmap_test", + ) + data.head(100) + +.. raw:: html + :file: /project/data/VerticaPy/docs/figures/ug_fs_complex_data.html + +We can also use the handy `genSQL` parameter to generate (but not execute) the SQL needed to create the final relation: .. note:: This is a great way to customize the data ingestion or alter the final relation types. +.. code-block:: python + + data = vp.read_json( + path + "laliga/2008.json", + schema = "public", + ingest_local = False, + use_complex_dt = True, + genSQL = True, + ) + .. code-block:: SQL CREATE TABLE "complex_vmap_test"."laliga_2005" ( @@ -174,16 +165,11 @@ We can also use the handy ``genSQL`` parameter to generate FROM '/scratch_b/qa/ericsson/laliga/2005.json' PARSER FJsonParser(); - - Feature Exploration --------------------- - -In the generated SQL from the above example, we can see -that the ``away_team`` column is a ROW type with a complex -structure consisting of many sub-columns. We can convert -this column into a JSON and view its contents: +In the generated SQL from the above example, we can see that the ``away_team`` column is a ROW type with a complex +structure consisting of many sub-columns. We can convert this column into a JSON and view its contents: .. code-block:: python @@ -200,7 +186,7 @@ this column into a JSON and view its contents: .. raw:: html :file: /project/data/VerticaPy/docs/figures/ug_fs_complex_data_2.html -As with a normal vDataFrame, we can easily extract the values from the sub-columns: +As with a normal :py:mod:`~verticapy.vDataFrame`, we can easily extract the values from the sub-columns: .. code-block:: python @@ -240,9 +226,7 @@ These nested structures can be used to create features: data["name_home"] = data["home_team"]["home_team_name"]; - -We can even flatten the nested structure inside a json file, -either flattening the entire file or just particular columns: +We can even flatten the nested structure inside a json file, either flattening the entire file or just particular columns: .. code-block:: python @@ -253,7 +237,7 @@ either flattening the entire file or just particular columns: ingest_local = False, flatten_maps = True, ) - data + data.head(100) .. ipython:: python :suppress: @@ -261,12 +245,14 @@ either flattening the entire file or just particular columns: vp.drop("complex_vmap_test.laliga_flat") path = "/project/data/VerticaPy/docs" path = path[0:-5] + "/verticapy/datasets/data/" - data = vp.read_json(path = path + "laliga/2008.json", - table_name = "laliga_flat", - schema = "complex_vmap_test", - ingest_local = True, - flatten_maps=True,) - res = data + data = vp.read_json( + path = path + "laliga/2008.json", + table_name = "laliga_flat", + schema = "complex_vmap_test", + ingest_local = True, + flatten_maps=True, + ) + res = data.head(100) html_file = open("/project/data/VerticaPy/docs/figures/ug_fs_complex_flatten.html", "w") html_file.write(res._repr_html_()) html_file.close() @@ -274,41 +260,29 @@ either flattening the entire file or just particular columns: .. raw:: html :file: /project/data/VerticaPy/docs/figures/ug_fs_complex_flatten.html -We can see that all the columns from the JSON file have been -flattened and multiple columns have been created for each -sub-column. This causes some loss in data structure, -but makes it easy to see the data and to use it for model building. +We can see that all the columns from the JSON file have been flattened and multiple columns have been created for each +sub-column. This causes some loss in data structure, but makes it easy to see the data and to use it for model building. -It is important to note that the data type of certain -columns (home_team.managers) is now ``VMap``, and not the ``ROW`` -type that we saw in the above cells. Even though both are -used to capture nested data, there is in a subtle difference between the two. +It is important to note that the data type of certain columns (home_team.managers) is now ``VMap``, and not the ``ROW`` +type that we saw in the above cells. Even though both are used to capture nested data, there is in a subtle difference between the two. -**VMap:** More flexible as it stores the data as a string of maps, -allowing the ingestion of data in varying shapes. The shape is -not fixed and new keys can easily be handled. This is a great -option when we don't know the structure in advance, or if the structure changes over time. +- **VMap:** More flexible as it stores the data as a string of maps, allowing the ingestion of data in varying shapes. The shape is not fixed and new keys can easily be handled. This is a great option when we don't know the structure in advance, or if the structure changes over time. -**Row:** More rigid because the dictionaries, including -all the data types, are fixed when they are defined. Newly -parsed keys are ignored. But because of it's rigid structure, -it is much more performant than VMaps. They are best used when -the file structure is known in advance. +- **Row:** More rigid because the dictionaries, including all the data types, are fixed when they are defined. Newly +parsed keys are ignored. But because of it's rigid structure, it is much more performant than VMaps. They are best used when the file structure is known in advance. - -To deconvolve the nested structure, we can use the ``flatten_arrays`` -parameter in order to make the output strictly formatted. However, it -can be an expensive process. +To deconvolve the nested structure, we can use the `flatten_arrays` parameter in order to make the output strictly formatted. However, it can be an expensive process. .. code-block:: python - vp.drop("complex_vmap_test.laliga_flat") - data = vp.read_json(path = path + "laliga/2008.json", - table_name = "laliga_flat", - schema = "complex_vmap_test", - ingest_local = False, - flatten_arrays=True,) - data + data = vp.read_json( + path = path + "laliga/2008.json", + table_name = "laliga_flat", + schema = "complex_vmap_test", + ingest_local = False, + flatten_arrays=True, + ) + data.head(100) .. ipython:: python :suppress: @@ -316,12 +290,14 @@ can be an expensive process. vp.drop("complex_vmap_test.laliga_flat") path = "/project/data/VerticaPy/docs" path = path[0:-5] + "/verticapy/datasets/data/" - data = vp.read_json(path = path + "laliga/2008.json", - table_name = "laliga_flat", - schema = "complex_vmap_test", - ingest_local = True, - flatten_arrays=True,) - res = data + data = vp.read_json( + path = path + "laliga/2008.json", + table_name = "laliga_flat", + schema = "complex_vmap_test", + ingest_local = True, + flatten_arrays=True, + ) + res = data.head(100) html_file = open("/project/data/VerticaPy/docs/figures/ug_fs_complex_flatten_arrays.html", "w") html_file.write(res._repr_html_()) html_file.close() @@ -349,7 +325,6 @@ We can even convert columns into other formats, such as string: Or integer: - .. code-block:: python data["match_week"].astype(int) @@ -368,9 +343,9 @@ Or integer: It is also possible to: -- Cast ``str`` to ``array`` -- Cast complex data types to ``json`` str -- Cast ``str`` to ``VMAP``s +- Cast ``str`` to ``array``. +- Cast complex data types to ``json`` str. +- Cast ``str`` to ``VMAP``s. - And much more... Multiple File Ingestion @@ -396,7 +371,7 @@ We can also do this for other file types. For example, csv: table_name = "cities_all", schema = "complex_vmap_test", ingest_local = False, - insert = True + insert = True, ) Materialize @@ -413,6 +388,7 @@ When we do not materialize a table, it automatically becomes a flextable: ingest_local = False, materialize = False, ) + data.head(100) .. ipython:: python :suppress: @@ -420,12 +396,14 @@ When we do not materialize a table, it automatically becomes a flextable: vp.drop("complex_vmap_test.laliga_verticapy_test_json") path = "/project/data/VerticaPy/docs" path = path[0:-5] + "/verticapy/datasets/data/" - data = vp.read_json(path = path + "laliga/*.json", - table_name = "laliga_verticapy_test_json", - schema = "complex_vmap_test", - ingest_local = True, - materialize = False,) - res = data + data = vp.read_json( + path = path + "laliga/*.json", + table_name = "laliga_verticapy_test_json", + schema = "complex_vmap_test", + ingest_local = True, + materialize = False, + ) + res = data.head(100) html_file = open("/project/data/VerticaPy/docs/figures/ug_fs_complex_materialize.html", "w") html_file.write(res._repr_html_()) html_file.close() @@ -433,7 +411,7 @@ When we do not materialize a table, it automatically becomes a flextable: .. raw:: html :file: /project/data/VerticaPy/docs/figures/ug_fs_complex_materialize.html -Some of the columns are VMAPs: +Some of the columns are ``VMAPs``: .. ipython:: python @@ -441,14 +419,12 @@ Some of the columns are VMAPs: for m in managers: print(data[m].isvmap()) -We can easily flatten the VMaps virtual columns by -using the :py:func:`vDataFrame.flat_vmap` method: +We can easily flatten the VMaps virtual columns by using the :py:func:`~vDataFrame.flat_vmap` method: .. code-block:: python data.flat_vmap(managers).drop(managers) - .. ipython:: python :suppress: @@ -458,8 +434,8 @@ using the :py:func:`vDataFrame.flat_vmap` method: html_file.write(res._repr_html_()) html_file.close() -.. .. raw:: html -.. :file: /project/data/VerticaPy/docs/figures/ug_fs_complex_materialize_flat.html +.. raw:: html + :file: /project/data/VerticaPy/docs/figures/ug_fs_complex_materialize_flat.html To check for a flex table, we can use the following function: @@ -469,8 +445,7 @@ To check for a flex table, we can use the following function: isflextable(table_name = "laliga_verticapy_test_json", schema = "complex_vmap_test") -We can then manually materialize the flextable using the -convenient :py:func:`vDataFrame.to_db` method: +We can then manually materialize the flextable using the convenient :py:func:`~vDataFrame.to_db` method: .. ipython:: python @@ -478,17 +453,14 @@ convenient :py:func:`vDataFrame.to_db` method: vp.drop("complex_vmap_test.laliga_to_db") data.to_db("complex_vmap_test.laliga_to_db"); -Once we have stored the database, we can easily create -a :py:func:`vDataFrame` of the relation: - +Once we have stored the database, we can easily create a :py:mod:`~verticapy.vDataFrame` of the relation: .. ipython:: python data_new = vp.vDataFrame("complex_vmap_test.laliga_to_db") - Transformations ------------------ +---------------- First, we load the dataset. @@ -510,12 +482,11 @@ First, we load the dataset. .. raw:: html :file: /project/data/VerticaPy/docs/figures/ug_fs_complex_cities.html -Once we have data in the form of :py:func:`vDataFrame`, -we can readily convert it to a ``JSON`` file: +Once we have data in the form of :py:mod:`~verticapy.vDataFrame`, we can readily convert it to a ``JSON`` file: .. ipython:: python - data.to_json(path = "amazon_json.json"); + data.to_json(path = "amazon_json.json") Now we can load the new JSON file and see the contents: @@ -540,8 +511,7 @@ Let's look at the begining portion of the string: json_str[0:100] -We can edit a portion of the string and save it again. -We'll change the name of the first State from ACRE to XXXX: +We can edit a portion of the string and save it again. We'll change the name of the first State from ACRE to XXXX: .. ipython:: python @@ -551,7 +521,6 @@ Now we can save this edited strings file: .. ipython:: python - out_file = open(path + "amazon_edited.json", "w") out_file.write(json_str) out_file.close() @@ -559,7 +528,8 @@ Now we can save this edited strings file: If we look at the new file, we can see the updated changes: .. ipython:: python - + + @suppress vp.drop("complex_vmap_test.amazon_edit") data = vp.read_json( path = path + "amazon_edited.json", diff --git a/docs/source/user_guide_full_stack_geopandas.rst b/docs/source/user_guide_full_stack_geopandas.rst index 700d36c15..e86be0cf1 100644 --- a/docs/source/user_guide_full_stack_geopandas.rst +++ b/docs/source/user_guide_full_stack_geopandas.rst @@ -4,7 +4,7 @@ Integrating with GeoPandas =========================== -As of version 0.4.0, VerticaPy features GeoPandas integration. This allows you to easily export a :py:mod:`vDataFrame` as a GeoPandas DataFrame, giving you more control over geospatial data. +As of version 0.4.0, VerticaPy features GeoPandas integration. This allows you to easily export a :py:mod:`~verticapy.vDataFrame` as a GeoPandas DataFrame, giving you more control over geospatial data. This example demonstrates the advantages of GeoPandas integration with the 'world' dataset. diff --git a/docs/source/user_guide_full_stack_linear_regression.rst b/docs/source/user_guide_full_stack_linear_regression.rst index 2235130bc..ba8df0763 100644 --- a/docs/source/user_guide_full_stack_linear_regression.rst +++ b/docs/source/user_guide_full_stack_linear_regression.rst @@ -438,7 +438,7 @@ Example with decomposition Let's look at the same dataset, but use decomposition techniques to filter out unimportant information. We don't have to normalize our data or look at correlations with these types of methods. -We'll begin by repeating the data preparation process of the previous section and export the resulting :py:mod:`vDataFrame` to Vertica. +We'll begin by repeating the data preparation process of the previous section and export the resulting :py:mod:`~verticapy.vDataFrame` to Vertica. .. code-block:: ipython @@ -526,7 +526,7 @@ We'll begin by repeating the data preparation process of the previous section an .. raw:: html :file: /project/data/VerticaPy/docs/figures/ug_fs_table_lr_16.html -Let's create our principal component analysis (:py:func:`~verticapy.machine_learning.vertica.PCA`) model. +Let's create our principal component analysis (:py:mod:`~verticapy.machine_learning.vertica.PCA`) model. .. code-block:: ipython @@ -560,7 +560,7 @@ Let's create our principal component analysis (:py:func:`~verticapy.machine_lear .. raw:: html :file: /project/data/VerticaPy/docs/figures/ug_fs_table_lr_17.html -We can verify the Gauss-Markov assumptions with our :py:func:`~verticapy.machine_learning.vertica.PCA` model. +We can verify the Gauss-Markov assumptions with our :py:mod:`~verticapy.machine_learning.vertica.PCA` model. .. code-block:: python @@ -610,4 +610,4 @@ As you can see, we've created a much more accurate model here than in our first Conclusion ----------- -We've seen two techniques that can help us create powerful linear regression models. While the first method normalized the data and looked for correlations, the second method applied a :py:func:`~verticapy.machine_learning.vertica.PCA` model. The second one allows us to confirm the Gauss-Markov assumptions - an essential part of using linear models. \ No newline at end of file +We've seen two techniques that can help us create powerful linear regression models. While the first method normalized the data and looked for correlations, the second method applied a :py:mod:`~verticapy.machine_learning.vertica.PCA` model. The second one allows us to confirm the Gauss-Markov assumptions - an essential part of using linear models. \ No newline at end of file diff --git a/docs/source/user_guide_full_stack_to_json.rst b/docs/source/user_guide_full_stack_to_json.rst index 098bf77a3..24b3c5df4 100644 --- a/docs/source/user_guide_full_stack_to_json.rst +++ b/docs/source/user_guide_full_stack_to_json.rst @@ -66,14 +66,14 @@ To create a vDataFrame out of a table in your Vertica database, specify its sche Create an XGB model ------------------- -Create a :py:func:`~verticapy.machine_learning.vertica.ensemble.XGBClassifier` model. +Create a :py:mod:`~verticapy.machine_learning.vertica.ensemble.XGBClassifier` model. Unlike a vDataFrame object, which simply queries the table it -was created with, the VerticaPy :py:func:`~verticapy.machine_learning.vertica.ensemble.XGBClassifier` object creates +was created with, the VerticaPy :py:mod:`~verticapy.machine_learning.vertica.ensemble.XGBClassifier` object creates and then references a model in Vertica, so it must be stored in a schema like any other database object. -This example creates the 'my_model' :py:func:`~verticapy.machine_learning.vertica.ensemble.XGBClassifier` model in +This example creates the 'my_model' :py:mod:`~verticapy.machine_learning.vertica.ensemble.XGBClassifier` model in the 'xgb_to_json' schema: This example loads the Titanic dataset with the load_titanic function @@ -204,7 +204,7 @@ Clean the Example Environment Drop the 'xgb_to_json' schema, using CASCADE to drop any database objects stored inside (the 'titanic' table, the -:py:func:`~verticapy.machine_learning.vertica.ensemble.XGBClassifier` +:py:mod:`~verticapy.machine_learning.vertica.ensemble.XGBClassifier` model, etc.), then delete the 'exported_xgb_model.json' file: .. ipython:: python diff --git a/docs/source/user_guide_full_stack_vdataframe_magic.rst b/docs/source/user_guide_full_stack_vdataframe_magic.rst index bb180d88d..3683f2650 100644 --- a/docs/source/user_guide_full_stack_vdataframe_magic.rst +++ b/docs/source/user_guide_full_stack_vdataframe_magic.rst @@ -4,7 +4,7 @@ The 'Magic' Methods of the vDataFrame ====================================== -VerticaPy 0.3.2 introduces the 'Magic' methods, which offer some additional flexilibility for mathematical operations in the :py:mod:`vDataFrame`. These methods let you handle many operations in a 'pandas-like' or Pythonic style. +VerticaPy 0.3.2 introduces the 'Magic' methods, which offer some additional flexilibility for mathematical operations in the :py:mod:`~verticapy.vDataFrame`. These methods let you handle many operations in a 'pandas-like' or Pythonic style. .. code-block:: ipython @@ -246,7 +246,7 @@ Not Equal Operator (!=) 'Pythonic' Invokation of Vertica Functions ------------------------------------------- -You can easily apply Vertica functions to your :py:mod:`vDataFrame`. Here, we use Vertica's COALESCE function to impute the 'age' of the passengers in our dataset. +You can easily apply Vertica functions to your :py:mod:`~verticapy.vDataFrame`. Here, we use Vertica's COALESCE function to impute the 'age' of the passengers in our dataset. .. code-block:: ipython @@ -264,7 +264,7 @@ You can easily apply Vertica functions to your :py:mod:`vDataFrame`. Here, we us Slicing the vDataFrame ----------------------- -You can now slice the :py:mod:`vDataFrame` with indexing operators. +You can now slice the :py:mod:`~verticapy.vDataFrame` with indexing operators. .. code-block:: ipython diff --git a/docs/source/user_guide_introduction.rst b/docs/source/user_guide_introduction.rst index 7855de83b..bbbcbf34c 100644 --- a/docs/source/user_guide_introduction.rst +++ b/docs/source/user_guide_introduction.rst @@ -25,7 +25,7 @@ Introduction .. grid-item:: .. card:: The virtual DataFrame - :link: notebooks/introduction/vdf/index.html + :link: user_guide.introduction.vdf :text-align: center :class-card: custom-card-9 diff --git a/docs/source/user_guide_introduction_best_practices.rst b/docs/source/user_guide_introduction_best_practices.rst index 66cb6eb35..6159c565a 100644 --- a/docs/source/user_guide_introduction_best_practices.rst +++ b/docs/source/user_guide_introduction_best_practices.rst @@ -10,9 +10,9 @@ Restrict objects and operations to essential columns As VerticaPy is effectively an abstraction of SQL, any database-level optimizations you make in your Vertica database carry over to VerticaPy. In Vertica, optimization is centered on projections, which are collections of table columns—from one or more tables—stored on disk in a format that optimizes query execution. When you write queries in terms of the original tables, the query uses the projections to return query results. For details about creating and designing projections, see the Projections section in the Vertica documentation. -Projections are created and managed in the Vertica database, but you can leverage the power of projections in VerticaPy with features such as the :py:mod:`vDataFrame`'s usecols parameter, which specifies the columns from the input relation to include in the :py:mod:`vDataFrame`. As columnar databases perform better when there are fewer columns in the query, especially when you are working with large datasets, limiting :py:mod:`vDataFrame` and operations to essential columns can lead to a significant performance improvement. By default, most :py:mod:`vDataFrame` methods use all numerical columns in the :py:mod:`vDataFrame`, but you can restrict the operation to specific columns. +Projections are created and managed in the Vertica database, but you can leverage the power of projections in VerticaPy with features such as the :py:mod:`~verticapy.vDataFrame`'s usecols parameter, which specifies the columns from the input relation to include in the :py:mod:`~verticapy.vDataFrame`. As columnar databases perform better when there are fewer columns in the query, especially when you are working with large datasets, limiting :py:mod:`~verticapy.vDataFrame` and operations to essential columns can lead to a significant performance improvement. By default, most :py:mod:`~verticapy.vDataFrame` methods use all numerical columns in the :py:mod:`~verticapy.vDataFrame`, but you can restrict the operation to specific columns. -In the following examples, we'll demonstrate how to create a `vDataFrame` from specific columns in the input relation, and then run methods on that :py:mod:`vDataFrame`. First, load the titanic dataset into Vertica using the :py:func:`~verticapy.datasets.load_titanic` function: +In the following examples, we'll demonstrate how to create a `vDataFrame` from specific columns in the input relation, and then run methods on that :py:mod:`~verticapy.vDataFrame`. First, load the titanic dataset into Vertica using the :py:func:`~verticapy.datasets.load_titanic` function: .. code-block:: python @@ -78,7 +78,7 @@ To turn off the SQL code generation option: # Turning off SQL. vp.set_option("sql_on", False) -To restrict the operation to specific columns in the :py:mod:`vDataFrame`, provide the column names in the `columns` parameter: +To restrict the operation to specific columns in the :py:mod:`~verticapy.vDataFrame`, provide the column names in the `columns` parameter: .. code-block:: python @@ -105,7 +105,7 @@ Instead of specifying essential columns to include, some methods allow you to li .. note:: - To list all columns in a :py:mod:`vDataFrame`, including non-numerical columns, use the :py:func:`~verticapy.vDataFrame.get_columns` method. + To list all columns in a :py:mod:`~verticapy.vDataFrame`, including non-numerical columns, use the :py:func:`~verticapy.vDataFrame.get_columns` method. You can then use this truncated list of columns in another method call; for instance, to compute a correlation matrix: @@ -126,12 +126,12 @@ You can then use this truncated list of columns in another method call; for inst Save the current relation -------------------------- -The :py:mod:`vDataFrame` works like a `view`, a stored query that encapsulates one or more SELECT statements. +The :py:mod:`~verticapy.vDataFrame` works like a `view`, a stored query that encapsulates one or more SELECT statements. If the generated relation uses many different functions, the computation time for each method call is greatly increased. Small transformations don't drastically slow down computation, but heavy transformations (multiple joins, frequent use of advanced analytical funcions, moving windows, etc.) can result in noticeable slowdown. When performing computationally expensive operations, you can aid performance by saving the vDataFrame structure as a table in the Vertica database. We will demonstrate this process in the following example. -First, create a :py:mod:`vDataFrame`, then perform some operations on that :py:mod:`vDataFrame`: +First, create a :py:mod:`~verticapy.vDataFrame`, then perform some operations on that :py:mod:`~verticapy.vDataFrame`: .. code-block:: python @@ -162,7 +162,7 @@ To understand how Vertica executes the different aggregations in the above relat Looking at the plan and its associated relation, it's clear that the transformations we applied to the vDataFrame result in a complicated relation. -Each method call to the :py:mod:`vDataFrame` must use this relation for computation. +Each method call to the :py:mod:`~verticapy.vDataFrame` must use this relation for computation. .. note:: @@ -406,13 +406,13 @@ To monitor how VerticaPy is computing the aggregations, use the :py:func:`~verti VerticaPy allows you to send multiple queries, either iteratively or concurrently, to the database when computing aggregations. -First, let's send a single query to compute the average for all columns in the :py:mod:`vDataFrame`: +First, let's send a single query to compute the average for all columns in the :py:mod:`~verticapy.vDataFrame`: .. ipython:: python display(vdf.avg(ncols_block = 20)) -We see that there was one SELECT query for all columns in the :py:mod:`vDataFrame`. +We see that there was one SELECT query for all columns in the :py:mod:`~verticapy.vDataFrame`. You can reduce the impact on the system by using the `ncols_block` parameter to split the computation into multiple iterative queries, where the value of the parameter is the number of columns included in each query. For example, setting `ncols_block` to 5 will split the computation, which consists of 20 total columns, into 4 separate queries, each of which computes the average for 5 columns: diff --git a/docs/source/user_guide_introduction_vdf.rst b/docs/source/user_guide_introduction_vdf.rst new file mode 100644 index 000000000..2b86b81a9 --- /dev/null +++ b/docs/source/user_guide_introduction_vdf.rst @@ -0,0 +1,514 @@ +.. _user_guide.introduction.vdf: + +The Virtual DataFrame +===================== + +The Virtual DataFrame (vDataFrame) is the core object of the VerticaPy library. Leveraging the power of Vertica and the flexibility of Python, the :py:mod:`~verticapy.vDataFrame` is a Python object that lets you manipulate the data representation in a Vertica database without modifying the underlying data. The data represented by a :py:mod:`~verticapy.vDataFrame` remains in the Vertica database, bypassing the limitations of working memory. When a :py:mod:`~verticapy.vDataFrame` is created or altered, VerticaPy formulates the operation as an SQL query and pushes the computation to the Vertica database, harnessing Vertica's massive parallel processing and in-built functions. Vertica then aggregates and returns the result to VerticaPy. In essence, vDataFrames behave similar to `views `_ in the Vertica database. + +For more information about Vertica's performance advantages, including its columnar orientation and parallelization across +nodes, see the `Vertica documentation `_. + +In the following tutorial, we will introduce the basic functionality of the :py:mod:`~verticapy.vDataFrame` and then explore the ways in which they utilize in-database processing to enhance performance. + +Creating vDataFrames +--------------------- + +First, run the :py:func:`~verticapy.datasets.load_titanic` function to ingest into +Vertica a dataset with information about titanic passengers: + +.. code-block:: python + + from verticapy.datasets import load_titanic + + load_titanic() + +.. ipython:: python + :suppress: + + from verticapy.datasets import load_titanic + res = load_titanic() + html_file = open("/project/data/VerticaPy/docs/figures/user_guide_introduction_best_practices_laod_titanic.html", "w") + html_file.write(res._repr_html_()) + html_file.close() + +.. raw:: html + :file: /project/data/VerticaPy/docs/figures/user_guide_introduction_best_practices_laod_titanic.html + +You can create a :py:mod:`~verticapy.vDataFrame` from either an existing relation or a customized relation. + +To create a :py:mod:`~verticapy.vDataFrame` using an existing relation, in this case the Titanic dataset, provide the name of the dataset: + +.. code-block:: python + + import verticapy as vp + + vp.vDataFrame("public.titanic") + +To create a :py:mod:`~verticapy.vDataFrame` using a customized relation, specify the SQL query for that relation as the argument: + +.. code-block:: python + + vp.vDataFrame("SELECT pclass, AVG(survived) AS survived FROM titanic GROUP BY 1") + +.. ipython:: python + :suppress: + + import verticapy as vp + res = vp.vDataFrame("SELECT pclass, AVG(survived) AS survived FROM titanic GROUP BY 1") + html_file = open("/project/data/VerticaPy/docs/figures/ug_intro_vdf_1.html", "w") + html_file.write(res._repr_html_()) + html_file.close() + +.. raw:: html + :file: /project/data/VerticaPy/docs/figures/ug_intro_vdf_1.html + +For more examples of creating vDataFrames, see :py:mod:`~verticapy.vDataFrame`. + +In-memory vs. in-database +-------------------------- + +The following examples demonstrate the performance advantages of loading and processing data in-database versus in-memory. + +First, we download the `Expedia dataset `_ from Kaggle and then load it into Vertica: + +.. note:: + + In this example, we are only taking a subset of the entire dataset to save time. + But as per our studies, the benifits increase exponentially if the size of the data gets larger. + +.. code-block:: python + + vp.read_csv("expedia.csv", schema = "public", parse_nrows = 20000000) + +Once the data is loaded into the Vertica database, we can create a :py:mod:`~verticapy.vDataFrame` using the relation that contains the Expedia dataset: + +.. ipython:: python + + import time + @suppress + vp.drop("public.expedia") + @suppress + vp.read_csv( + "/project/data/VerticaPy/docs/source/_static/website/examples/data/booking/expedia.csv", + schema = "public", + parse_nrows = 20000000, + ) + start_time = time.time() + expedia = vp.vDataFrame("public.expedia") + print("elapsed time = {}".format(time.time() - start_time)) + +The :py:mod:`~verticapy.vDataFrame` was created in about a second. All the data—about 4GB—is stored in Vertica, requiring no in-memory data loading. + +Now, to compare the above result with in-memory loading, we load about half the dataset into pandas: + +.. note:: + + This process is expensive on local machines, so + avoid running the following code if your computer + has less than 2GB of memory. + +.. code-block:: python + + import pandas as pd + + L_nrows = [10000, 100000, 149814] + L_time = [] + for nrows in L_nrows: + start_time = time.time() + expedia_df = pd.read_csv("expedia.csv", nrows = nrows) + elapsed_time = time.time() - start_time + L_time.append(elapsed_time) + +.. code-block:: python + + import pandas as pd + + L_nrows = [10000, 100000, 149814] + L_time = [] + for nrows in L_nrows: + start_time = time.time() + expedia_df = pd.read_csv( + "expedia.csv", + nrows = nrows, + ) + elapsed_time = time.time() - start_time + L_time.append(elapsed_time) + +.. ipython:: python + :suppress: + + import pandas as pd + + L_nrows = [10000, 100000, 149814] + L_time = [] + for nrows in L_nrows: + start_time = time.time() + expedia_df = pd.read_csv( + "/project/data/VerticaPy/docs/source/_static/website/examples/data/booking/expedia.csv", + nrows = nrows, + ) + elapsed_time = time.time() - start_time + L_time.append(elapsed_time) + +.. ipython:: python + + for i in range(len(L_time)): + print("nrows = {}; elapsed time = {}".format(L_nrows[i], L_time[i])) + +It took an order of magnitude more to load into memory compared with the time required to create the :py:mod:`~verticapy.vDataFrame`. Loading data into pandas is quite fast when the data volume is low (less than some MB), but as the size of the dataset increases, the load time can become exponentially more expensive, as seen in the following plot: + +.. ipython:: python + + import matplotlib.pyplot as plt + @savefig ug_intro_vdf_plot + plt.plot(L_nrows, L_time) + @savefig ug_intro_vdf_plot_2 + plt.show() + +Even after the data is loaded into memory, the performance is very slow. The following example removes non-numeric columns from the dataset, then computes a correlation matrix: + +.. ipython:: python + + columns_to_drop = ['date_time', 'srch_ci', 'srch_co'] ; + expedia_df = expedia_df.drop(columns_to_drop, axis=1); + start_time = time.time() + expedia_df.corr(); + print(f"elapsed time = {time.time() - start_time}") + +Let's compare the performance in-database using a :py:mod:`~verticapy.vDataFrame` to compute the correlation matrix of the entire dataset: + +.. ipython:: python + + # Remove non-numeric columns + expedia.drop(columns = ['date_time', 'srch_ci', 'srch_co']); + start_time = time.time() + expedia.corr(show = False); + print(f"elapsed time = {time.time() - start_time}") + +VerticaPy also caches the computed aggregations. With this cache available, we can repeat the correlation matrix computation almost instantaneously: + +.. note:: + + If necessary, you can deactivate the cache by calling the :py:func:`~verticapy.set_option` function with the `cache` parameter set to False. + +.. ipython:: python + + start_time = time.time() + expedia.corr(show = False); + print(f"elapsed time = {time.time() - start_time}") + +Memory usage ++++++++++++++ + +Now, we will examine how the memory usage compares between in-memory and in-database. + +First, use the pandas `info()` method to explore the DataFrame's memory usage: + +.. ipython:: python + + expedia_df.info() + +Compare this with vDataFrame: + +.. code-block:: python + + expedia.memory_usage() + +.. ipython:: python + :suppress: + + res = expedia.memory_usage() + html_file = open("/project/data/VerticaPy/docs/figures/ug_intro_vdf_mem.html", "w") + html_file.write(res._repr_html_()) + html_file.close() + +.. raw:: html + :file: /project/data/VerticaPy/docs/figures/ug_intro_vdf_mem.html + +The :py:mod:`~verticapy.vDataFrame` only uses about 37KB! By storing the data in the Vertica database, and only recording the +user's data modifications in memory, the memory usage is reduced to a minimum. + +With VerticaPy, we can take advantage of Vertica's structure and scalability, providing fast queries without ever loading the data into memory. In the above examples, we've seen that in-memory processing is much more expensive in both computation and memory usage. This often leads to the decesion to downsample the data, which sacrfices the possibility of further data insights. + +The :py:mod:`~verticapy.vDataFrame` structure +---------------------------------------------- + +Now that we've seen the performance and memory benefits of the vDataFrame, let's dig into some of the underlying structures and methods that produce these great results. + +:py:mod:`~verticapy.vDataFrame`s are composed of columns called :py:mod:`vDataColumn`s. To view all :py:mod:`vDataColumn`s in a :py:mod:`~verticapy.vDataFrame`, use the :py:func:`~verticapy.get_columns` method: + +.. ipython:: python + + expedia.get_columns() + +To access a :py:mod:`~verticapy.vDataColumn`, specify the column name in square brackets, for example: + +.. note:: + + VerticaPy saves computed aggregations to avoid unncessary recomputations. + +.. code-block:: python + + expedia["is_booking"].describe() + +.. ipython:: python + :suppress: + + res = expedia["is_booking"].describe() + html_file = open("/project/data/VerticaPy/docs/figures/ug_intro_vdf_describe.html", "w") + html_file.write(res._repr_html_()) + html_file.close() + +.. raw:: html + :file: /project/data/VerticaPy/docs/figures/ug_intro_vdf_describe.html + +Each :py:mod:`~verticapy.vDataColumn` has its own catalog to save user modifications. In the previous example, we computed +some aggregations for the ``is_booking`` column. Let's look at the catalog for that :py:mod:`~verticapy.vDataColumn`: + +.. ipython:: python + + expedia["is_booking"]._catalog + +The catalog is updated whenever major changes are made to the data. + +We can also view the vDataFrame's backend SQL code generation by setting the `sql_on` parameter to ``True`` with the :py:func:`~verticapy.set_option` function: + +.. code-block:: python + + vp.set_option("sql_on", True) + expedia["cnt"].describe() + +.. code-block:: sql + + -- Computing the different aggregations + SELECT + /*+LABEL('vDataframe.aggregate')*/ + APPROXIMATE_COUNT_DISTINCT("cnt") + FROM ( + SELECT + "site_name", + "posa_continent", + "user_location_country", + "user_location_region", + "user_location_city", + "orig_destination_distance", + "user_id", + "is_mobile", + "is_package", + "channel", + "srch_adults_cnt", + "srch_children_cnt", + "srch_rm_cnt", + "srch_destination_id", + "srch_destination_type_id", + "is_booking", + "cnt", + "hotel_continent", + "hotel_country", + "hotel_market", + "hotel_cluster" + FROM "public"."expedia" + ) VERTICAPY_SUBTABLE + LIMIT 1; + + -- Computing the descriptive statistics of all numerical columns using SUMMARIZE_NUMCOL + SELECT + /*+LABEL('vDataframe.describe')*/ + SUMMARIZE_NUMCOL("cnt") OVER () + FROM ( + SELECT + "site_name", + "posa_continent", + "user_location_country", + "user_location_region", + "user_location_city", + "orig_destination_distance", + "user_id", + "is_mobile", + "is_package", + "channel", + "srch_adults_cnt", + "srch_children_cnt", + "srch_rm_cnt", + "srch_destination_id", + "srch_destination_type_id", + "is_booking", + "cnt", + "hotel_continent", + "hotel_country", + "hotel_market", + "hotel_cluster" + FROM "public"."expedia" + ) VERTICAPY_SUBTABLE; + +.. ipython:: python + :suppress: + + res = expedia["cnt"].describe() + html_file = open("/project/data/VerticaPy/docs/figures/ug_intro_vdf_describe_cnt.html", "w") + html_file.write(res._repr_html_()) + html_file.close() + +.. raw:: html + :file: /project/data/VerticaPy/docs/figures/ug_intro_vdf_describe_cnt.html + +To control whether each query outputs its elasped time, use the ``time_on`` parameter of the :py:func:`~verticapy.set_option` function: + +.. ipython:: python + + vp.set_option("sql_on", False) + expedia = vp.vDataFrame("public.expedia") # creating a new vDataFrame to delete the catalog + vp.set_option("time_on", True) + expedia.corr() + +The aggregation's for each vDataColumn are saved to its catalog. If we again call the :py:func:`~verticapy.vDataFrame.corr` method, it'll complete in a couple seconds—the time needed to draw the graphic—because the aggregations have already been computed and saved during the last call: + +.. ipython:: python + + start_time = time.time() + expedia.corr(); + print("elapsed time = {}".format(time.time() - start_time)) + +To turn off the elapsed time and the SQL code generation options: + +.. ipython:: python + + vp.set_option("sql_on", False) + vp.set_option("time_on", False) + +You can obtain the current :py:mod:`~verticapy.vDataFrame` relation with the :py:func:`~verticapy.vDataFrame.current_relation` method: + +.. ipython:: python + + print(expedia.current_relation()) + +The generated SQL for the relation changes according to the user's modifications. For example, if we impute the missing values of the ``orig_destination_distance`` vDataColumn by its average and then drop the ``is_package`` vDataColumn, these changes are reflected in the relation: + +.. ipython:: python + + expedia["orig_destination_distance"].fillna(method = "avg"); + expedia["is_package"].drop(); + print(expedia.current_relation()) + +Notice that the ``is_package`` column has been removed from the ``SELECT`` statement and the ``orig_destination_distance`` is now using a ``COALESCE SQL`` function. + +vDataFrame attributes and management +------------------------------------- + +The :py:mod:`~verticapy.vDataFrame` has many attributes and methods, some of which were demonstrated in the above examples. :py:mod:`~verticapy.vDataFrame`s have two types of attributes: + +- Virtual Columns (vDataColumn) +- Main attributes (columns, main_relation ...) + +The vDataFrame's main attributes are stored in the ``_vars`` dictionary: + +.. note:: You should never change these attributes manually. + +.. ipython:: python + + expedia._vars + +Data types +----------- + +:py:mod:`~verticapy.vDataFrame`s use the data types of its :py:mod:`~verticapy.vDataColumn`s. The behavior of some :py:mod:`~verticapy.vDataFrame` methods depend on the data type of the columns. + +For example, computing a histogram for a numerical data type is not the same as computing a histogram for a categorical data type. + +The :py:mod:`~verticapy.vDataFrame` identifies four main data types: + +- ``int``: integers are treated like categorical data types + when their cardinality is low; otherwise, they are considered numeric +- ``float``: numeric data types +- ``date``: date-like data types (including timestamp) +- ``text``: categorical data types + +Data types not included in the above list are automatically +treated as categorical. You can examine the data types of +the vDataColumns in a :py:mod:`~verticapy.vDataFrame` using the +:py:func:`~verticapy.vDataFrame.dtypes` method: + +.. code-block:: python + + expedia.dtypes() + +.. ipython:: python + :suppress: + + res = expedia.dtypes() + html_file = open("/project/data/VerticaPy/docs/figures/ug_intro_vdf_expedia_dtypes.html", "w") + html_file.write(res._repr_html_()) + html_file.close() + +.. raw:: html + :file: /project/data/VerticaPy/docs/figures/ug_intro_vdf_expedia_dtypes.html + +To convert the data type of a vDataColumn, use the :py:func:`~verticapy.vDataColumn.astype` method: + +.. ipython:: python + + expedia["hotel_market"].astype("varchar"); + expedia["hotel_market"].ctype() + +To view the category of a specific :py:mod:`~verticapy.vDataColumn`, specify the :py:mod:`~verticapy.vDataColumn` and use the :py:func:`~verticapy.vDataColumn.category` method: + +.. ipython:: python + + expedia["hotel_market"].category() + +Exporting, saving, and loading +------------------------------- + +The :py:func:`~verticapy.vDataFrame.save` and :py:func:`~verticapy.vDataFrame.load` functions allow you to save and load vDataFrames: + +.. code-block:: python + + expedia.save() + expedia.filter("is_booking = 1") + +.. ipython:: python + :suppress: + + expedia.save() + res = expedia.filter("is_booking = 1") + html_file = open("/project/data/VerticaPy/docs/figures/ug_intro_vdf_expedia_filter.html", "w") + html_file.write(res._repr_html_()) + html_file.close() + +.. raw:: html + :file: /project/data/VerticaPy/docs/figures/ug_intro_vdf_expedia_filter.html + +To return a :py:mod:`~verticapy.vDataFrame` to a previously saved structure, use the :py:func:`~verticapy.vDataFrame.load` function: + +.. ipython:: python + + expedia = expedia.load(); + print(expedia.shape()) + +Because :py:mod:`~verticapy.vDataFrame`s are views of data stored in the connected Vertica database, any modifications made to the :py:mod:`~verticapy.vDataFrame` are not reflected in the underlying data in the database. To save a :py:mod:`~verticapy.vDataFrame`'s relation to the database, use the :py:func:`~verticapy.vDataFrame.to_db` method. + +It's good practice to examine the expected disk usage of the :py:mod:`~verticapy.vDataFrame` before exporting it to the database: + +.. code-block:: python + + expedia.expected_store_usage(unit = "Gb") + +.. ipython:: python + :suppress: + + res = expedia.expected_store_usage(unit = "Gb") + html_file = open("/project/data/VerticaPy/docs/figures/ug_intro_vdf_expedia_storage_gb.html", "w") + html_file.write(res._repr_html_()) + html_file.close() + +.. raw:: html + :file: /project/data/VerticaPy/docs/figures/ug_intro_vdf_expedia_storage_gb.html + +If you decide that there is sufficient space to store the :py:mod:`~verticapy.vDataFrame` in the database, run the :py:func:`~verticapy.vDataFrame.to_db` method: + +.. code-block:: python + + expedia.to_db( + "public.expedia_clean", + relation_type = "table", + ) \ No newline at end of file diff --git a/docs/source/user_guide_machine_learning_introduction.rst b/docs/source/user_guide_machine_learning_introduction.rst index 6f95449c4..cdae9f98c 100644 --- a/docs/source/user_guide_machine_learning_introduction.rst +++ b/docs/source/user_guide_machine_learning_introduction.rst @@ -100,7 +100,7 @@ When we have more than two categories, we use the expression 'Multiclass Classif Unsupervised Learning ---------------------- -These algorithms are to used to segment the data (``k-means``, :py:func:`~verticapy.machine_learning.vertica.DBSCAN`, etc.) or to detect anomalies (:py:func:`~verticapy.machine_learning.vertica.LocalOutlierFactor`, ``Z-Score`` Techniques...). In particular, they're useful for finding patterns in data without labels. For example, let's use a k-means algorithm to create different clusters on the Iris dataset. Each cluster will represent a flower's species. +These algorithms are to used to segment the data (``k-means``, :py:mod:`~verticapy.machine_learning.vertica.DBSCAN`, etc.) or to detect anomalies (:py:mod:`~verticapy.machine_learning.vertica.LocalOutlierFactor`, ``Z-Score`` Techniques...). In particular, they're useful for finding patterns in data without labels. For example, let's use a k-means algorithm to create different clusters on the Iris dataset. Each cluster will represent a flower's species. .. code-block:: python diff --git a/docs/source/user_guide_machine_learning_model_tracking.rst b/docs/source/user_guide_machine_learning_model_tracking.rst index aedd51034..bda1ca72e 100644 --- a/docs/source/user_guide_machine_learning_model_tracking.rst +++ b/docs/source/user_guide_machine_learning_model_tracking.rst @@ -112,7 +112,7 @@ scenarios where there is often a large number of leftover models. The :py:func:` my_experiment_1.drop(keeping_models=[top_model.model_name]) Experiments are also helpful for performing grid search on hyper-parameters. The following example shows how they can -be used to study the impact of the max_iter parameter on the prediction performance of :py:func:`~verticapy.machine_learning.vertica.LogisticRegression` models. +be used to study the impact of the max_iter parameter on the prediction performance of :py:mod:`~verticapy.machine_learning.vertica.LogisticRegression` models. .. ipython:: python :okwarning: diff --git a/docs/source/user_guide_machine_learning_time_series.rst b/docs/source/user_guide_machine_learning_time_series.rst index 0dfddf733..9d5e5c15f 100644 --- a/docs/source/user_guide_machine_learning_time_series.rst +++ b/docs/source/user_guide_machine_learning_time_series.rst @@ -83,7 +83,7 @@ To help visualize the seasonality of forest fires, we'll draw some autocorrelati Forest fires follow a predictable, seasonal pattern, so it should be easy to predict future forest fires with past data. -VerticaPy offers several models, including a multiple time series model. For this example, let's use a :py:func:`~verticapy.machine_learning.vertica.ARIMA` model. +VerticaPy offers several models, including a multiple time series model. For this example, let's use a :py:mod:`~verticapy.machine_learning.vertica.ARIMA` model. .. ipython:: python diff --git a/docs/source/user_guide_performance.rst b/docs/source/user_guide_performance.rst index 93bf9cd60..8d4a333d7 100644 --- a/docs/source/user_guide_performance.rst +++ b/docs/source/user_guide_performance.rst @@ -9,7 +9,8 @@ Performance .. grid-item:: .. card:: QueryProfiler - :link: notebooks/performance/queryprofiler/index.html + :link: user_guide.performance.qprof + :link-type: ref :text-align: center :class-card: custom-card-15 @@ -21,5 +22,5 @@ Performance :hidden: :maxdepth: 1 - notebooks/performance/queryprofiler/index.ipynb + user_guide_performance_qprof diff --git a/docs/source/user_guide_performance_qprof.rst b/docs/source/user_guide_performance_qprof.rst new file mode 100644 index 000000000..aca4e2679 --- /dev/null +++ b/docs/source/user_guide_performance_qprof.rst @@ -0,0 +1,506 @@ +.. _user_guide.performance.qprof: + +==================================== +Getting started with Query Profiler +==================================== + +This starter notebook will help you get up and running with the Query Profiler (:py:func:`~verticapy.performance.vertica.QueryProfiler`) tool in VerticaPyLab and demonstrate functionality through various examples and use cases. + +This tool is a work in progress and the VerticaPy team is continuously adding new features. + +See also :py:func:`~verticapy.performance.vertica.QueryProfiler`, +:py:func:`~verticapy.performance.vertica.QueryProfilerInterface`, +:py:func:`~verticapy.performance.vertica.QueryProfilerComparison`. + +VerticaPyLab +------------- + +The easiest way to use the :py:func:`~verticapy.performance.vertica.QueryProfiler` tool is through VerticaPyLab. For installation instructions, see :ref:`getting_started`. + +Before using the :py:func:`~verticapy.performance.vertica.QueryProfiler` tool, confirm that you are connected to a Vertica database. If not, follow the connection instructions in a Jupyter notebook or connect using the Connect option on the VerticaPyLab homepage. + +QueryProfiler +-------------- + +The :py:func:`~verticapy.performance.vertica.QueryProfiler` object is a python object that includes many built-in methods for analyzing queries and their performance. There are a few different ways to create a :py:func:`~verticapy.performance.vertica.QueryProfiler` object. + +Create and save a QueryProfiler object ++++++++++++++++++++++++++++++++++++++++ + +First, import the verticapy package and load the datasets: + +.. ipython:: python + + import verticapy as vp + from verticapy.datasets import load_titanic, load_amazon + # load datasets + titanic = load_titanic() + amazon = load_amazon() + +Create :py:func:`~verticapy.performance.vertica.QueryProfiler` object from transaction id and statement id ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ + +Before creating the :py:func:`~verticapy.performance.vertica.QueryProfiler` object, take a look at the data used by the query: + +.. code-block:: python + + amazon.head(100) + +.. ipython:: python + :suppress: + + res = amazon.head(100) + html_file = open("/project/data/VerticaPy/docs/figures/user_guides_performance_qprof_amazon.html", "w") + html_file.write(res._repr_html_()) + html_file.close() + +.. raw:: html + :file: /project/data/VerticaPy/docs/figures/user_guides_performance_qprof_amazon.html + +.. code-block:: python + + titanic.head(100) + +.. ipython:: python + :suppress: + + res = titanic.head(100) + html_file = open("/project/data/VerticaPy/docs/figures/user_guides_performance_qprof_titanic.html", "w") + html_file.write(res._repr_html_()) + html_file.close() + +.. raw:: html + :file: /project/data/VerticaPy/docs/figures/user_guides_performance_qprof_titanic.html + +We can now run some queries to create a :py:func:`~verticapy.performance.vertica.QueryProfiler` object. One way to do so is by using the queries `statement_id` and `transaction_id`. + +To allow for SQL execution in Jupyter cells, load the sql extension: + +.. ipython:: python + + %load_ext verticapy.sql + +Next, let us run the queries: + +.. code-block:: python + + %%sql + SELECT + date, + MONTH(date) as month, + AVG(number) as avg_number_test + FROM public.amazon + GROUP BY date + ORDER BY avg_number_test DESC; + +.. ipython:: python + :suppress: + + query = """ + SELECT + date, + MONTH(date) as month, + AVG(number) as avg_number_test + FROM public.amazon + GROUP BY date + ORDER BY avg_number_test DESC; + """ + res = vp.vDataFrame(query) + query_1 = query + html_file = open("/project/data/VerticaPy/docs/figures/user_guides_performance_qprof_sql.html", "w") + html_file.write(res._repr_html_()) + html_file.close() + +.. raw:: html + :file: /project/data/VerticaPy/docs/figures/user_guides_performance_qprof_sql.html + +.. code-block:: python + + %%sql + SELECT + a.date, + MONTH(a.date) AS month, + AVG(a.number) AS avg_number_test, + b.max_number + FROM + public.amazon AS a + JOIN ( + SELECT + date, + MAX(number) AS max_number + FROM + public.amazon + GROUP BY + date + ) AS b + ON + a.date = b.date + GROUP BY + a.date, b.max_number + ORDER BY + avg_number_test DESC; + +.. ipython:: python + :suppress: + + query = """ + SELECT + a.date, + MONTH(a.date) AS month, + AVG(a.number) AS avg_number_test, + b.max_number + FROM + public.amazon AS a + JOIN ( + SELECT + date, + MAX(number) AS max_number + FROM + public.amazon + GROUP BY + date + ) AS b + ON + a.date = b.date + GROUP BY + a.date, b.max_number + ORDER BY + avg_number_test DESC; + """ + query_2 = query + res = vp.vDataFrame(query_2) + html_file = open("/project/data/VerticaPy/docs/figures/user_guides_performance_qprof_sql_2.html", "w") + html_file.write(res._repr_html_()) + html_file.close() + +.. raw:: html + :file: /project/data/VerticaPy/docs/figures/user_guides_performance_qprof_sql_2.html + +In order to create a :py:func:`~verticapy.performance.vertica.QueryProfiler` object from a query, we need the queries statement_id and transaction_id, both of which are found in the QUERY_REQUESTS system table: + +.. code-block:: python + + from verticapy.performance.vertica import QueryProfiler, QueryProfilerInterface + + qprof = QueryProfiler((45035996273780927,76)) + +To create a :py:func:`~verticapy.performance.vertica.QueryProfiler` object w/ multiple queries, provide a list of tuples + +.. code-block:: python + + qprof = QueryProfilerInterface([(45035996273780927,74), (45035996273780075,6)]) + +Once the :py:func:`~verticapy.performance.vertica.QueryProfiler` object is created, you can run the get_queries() method to view the queries contained in the :py:func:`~verticapy.performance.vertica.QueryProfiler` object: + +.. code-block:: python + + qprof.get_queries() + +.. ipython:: python + :suppress: + :okwarning: + + from verticapy.performance.vertica import QueryProfiler, QueryProfilerInterface + qprof = QueryProfilerInterface([query_1, query_2]) + res = qprof.get_queries() + html_file = open("/project/data/VerticaPy/docs/figures/user_guides_performance_qprof_get_queries.html", "w") + html_file.write(res._repr_html_()) + html_file.close() + +.. raw:: html + :file: /project/data/VerticaPy/docs/figures/user_guides_performance_qprof_get_queries.html + +To visualize the query plan, run :py;func:`verticapy.QueryProfilerInterface.get_qplan_tree`, +which is customizable, allowing you to specify certain metrics or focus on a specified tree path: + +.. image:: ../../source/_static/website/user_guides/performance/user_guide_performance_qprof_get_qplan_tree.PNG + :width: 80% + :align: center + +Create a :py:func:`~verticapy.performance.vertica.QueryProfiler` object directly from a query +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ + +You can also create the :py:func:`~verticapy.performance.vertica.QueryProfiler` Object directly from an SQL Command: + +.. code-block:: python + + qprof = QueryProfiler(""" + select transaction_id, statement_id, request, request_duration + from query_requests where start_timestamp > (now() - interval'1 hour') + order by request_duration desc limit 10; + """ + ) + +Save the QueryProfiler object in a target schema ++++++++++++++++++++++++++++++++++++++++++++++++++ + +After you create a :py:func:`~verticapy.performance.vertica.QueryProfiler` object, you can save it to a target schema. + +In this example, we will save the object to the ``sc_demo`` schema: + +.. ipython:: python + + vp.create_schema("sc_demo") + +To save the :py:func:`~verticapy.performance.vertica.QueryProfiler` object, specify the `target_schema` and, optionally, a `key_id` (it is a unique key which is used to search for the stored Qprof object) when creating the :py:func:`~verticapy.performance.vertica.QueryProfiler` object: + +.. code-block:: python + + # Save it to your schema + qprof = QueryProfiler( + (45035996273780927, 76), + target_schema = "sc_demo", + key_id = "unique_xx1", + overwrite = True, + ) + +Load a :py:func:`~verticapy.performance.vertica.QueryProfiler` object +---------------------------------------------------------------------- + +To load a previously saved :py:func:`~verticapy.performance.vertica.QueryProfiler`, simply provide its `target_schema` and `key_id`: + +.. code-block:: python + + from verticapy.performance.vertica import QueryProfiler, QueryProfilerInterface + + # Someone else can now connect to my DB and use the object. + qprof = QueryProfiler( + target_schema = "sc_demo", + key_id = "unique_xx1", + ) + +Export and import +------------------ + +You can export and import :py:func:`~verticapy.performance.vertica.QueryProfiler` objects as .tar files. + +Export ++++++++ + +To export a :py:func:`~verticapy.performance.vertica.QueryProfiler` object, use the :py:func:`~verticapy.performance.vertica.QueryProfiler.export_profile` method: + +.. code-block:: python + + qprof.export_profile(filename="test_export_1.tar") + +.. note:: + + There is also a shell script which helps you export ``qprof`` data without python. See `qprof_export `_. + +Import ++++++++ + +To import a :py:func:`~verticapy.performance.vertica.QueryProfiler` object, use the :py:func:`~verticapy.performance.vertica.QueryProfiler.import_profile` method and provide the `target_schema` and `key_id`. + +Make sure the `key_id` is unique/unused. Let us create a new schema to load this into: + +.. code-block:: python + + vp.create_schema("sc_demo_1") + + qprof = QueryProfiler.import_profile( + target_schema = "sc_demo_1", + key_id = "unique_load_xx1", + filename = "test_export_1.tar", + auto_initialize = True, + ) + +Methods & attributes +--------------------- + +The :py:func:`~verticapy.performance.vertica.QueryProfiler` object includes many useful methods and attributes to aid in the analysis of query performence. + +Access performance tables +++++++++++++++++++++++++++ + +With the :py:func:`~verticapy.performance.vertica.QueryProfiler` object, you can access any of the following tables: + +.. ipython:: python + + qprof.get_table() + +For example, view the ``QUERY_EVENTS`` table: + +.. code-block:: python + + qprof.get_table("query_events") + +.. ipython:: python + :suppress: + + res = qprof.get_table("query_events") + html_file = open("/project/data/VerticaPy/docs/figures/user_guides_performance_qprof_query_events.html", "w") + html_file.write(res._repr_html_()) + html_file.close() + +.. raw:: html + :file: /project/data/VerticaPy/docs/figures/user_guides_performance_qprof_query_events.html + +Or the ``DC_EXPLAIN_PLANS`` table: + +.. code-block:: python + + qprof.get_table('dc_explain_plans') + +.. ipython:: python + :suppress: + + res = qprof.get_table('dc_explain_plans') + html_file = open("/project/data/VerticaPy/docs/figures/user_guides_performance_qprof_dc_explain_plans.html", "w") + html_file.write(res._repr_html_()) + html_file.close() + +.. raw:: html + :file: /project/data/VerticaPy/docs/figures/user_guides_performance_qprof_dc_explain_plans.html + +Or the ``QUERY_CONSUMPTION`` table: + +.. code-block:: python + + qprof.get_table("query_consumption") + +.. ipython:: python + :suppress: + + res = qprof.get_table("query_consumption") + html_file = open("/project/data/VerticaPy/docs/figures/user_guides_performance_qprof_query_consumption.html", "w") + html_file.write(res._repr_html_()) + html_file.close() + +.. raw:: html + :file: /project/data/VerticaPy/docs/figures/user_guides_performance_qprof_query_consumption.html + +Get query information +++++++++++++++++++++++ + +You can retrieve the query information, such as `transaction_id` and `statement_id`, from the :py:func:`~verticapy.performance.vertica.QueryProfiler` object: + +.. ipython:: python + :okwarning: + + qprof = QueryProfiler(""" + select transaction_id, statement_id, request, request_duration + from query_requests where start_timestamp > (now() - interval'1 hour') + order by request_duration desc limit 10; + """ + ) + +View the `transaction_id` and `statement_id`: + +.. ipython:: python + + tid = qprof.transaction_id + sid = qprof.statement_id + print(tid, sid) + +Display the query request: + +.. ipython:: python + + # Pretty, Formatted, Results + qprof.get_request() + +View the number of query steps in a bar graph: + +.. code-block:: python + + qprof.get_qsteps(kind="bar") + +.. ipython:: python + :suppress: + :okwarning: + + fig = qprof.get_qsteps(kind="bar") + html_text = fig.htmlcontent.replace("container", "user_guides_performance_qprof_bar") + with open("figures/user_guides_performance_qprof_bar.html", "w") as file: + file.write(html_text) + +.. raw:: html + :file: /project/data/VerticaPy/docs/figures/user_guides_performance_qprof_bar.html + +.. ipython:: python + + qprof.get_qplan() + +.. code-block:: python + + qprof.get_qplan_tree() + +To view the query plan profile in a pie chart: + +.. code-block:: python + + qprof.get_qplan_profile(kind = "pie") + +.. ipython:: python + :suppress: + :okwarning: + + vp.set_option("plotting_lib", "plotly") + fig = qprof.get_qplan_profile(kind = "pie") + fig.write_html("/project/data/VerticaPy/docs/figures/user_guides_performance_qprof_pie.html") + +.. raw:: html + :file: /project/data/VerticaPy/docs/figures/user_guides_performance_qprof_pie.html + +To view the cpu time of the query in a bar graph: + +.. code-block:: python + + qprof.get_cpu_time(kind="bar") + +.. ipython:: python + :suppress: + :okwarning: + + fig = qprof.get_cpu_time(kind="bar") + fig.write_html("/project/data/VerticaPy/docs/figures/user_guides_performance_qprof_cpu_bar.html") + +.. raw:: html + :file: /project/data/VerticaPy/docs/figures/user_guides_performance_qprof_cpu_bar.html + +:py:func:`~verticapy.performance.vertica.QueryProfiler` execution report ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ + +The :py:func:`~verticapy.performance.vertica.QueryProfiler` object can also generate a report that includes various performence metrics, including which operation took the most amount of time: + +.. code-block:: python + + qprof.get_qexecution_report().sort({"exec_time_us": "desc"}) + +.. ipython:: python + :suppress: + :okwarning: + + res = qprof.get_qexecution_report().sort({"exec_time_us": "desc"}) + html_file = open("/project/data/VerticaPy/docs/figures/user_guides_performance_qprof_query_report.html", "w") + html_file.write(res._repr_html_()) + html_file.close() + +.. raw:: html + :file: /project/data/VerticaPy/docs/figures/user_guides_performance_qprof_query_report.html + +To view the query execution details: + +.. code-block:: python + + qprof.get_qexecution() + +.. ipython:: python + :suppress: + :okwarning: + + fig = qprof.get_qexecution() + fig.write_html("/project/data/VerticaPy/docs/figures/user_guides_performance_qprof_last.html") + +.. raw:: html + :file: /project/data/VerticaPy/docs/figures/user_guides_performance_qprof_last.html + +:py:func:`~verticapy.performance.vertica.QueryProfiler` Summary Report Export +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ + +You can also easily export the entire report in an HTML format. This report can be read without having any connection to database or a jupyter environment making it very convenient to share and analyze offline. + +.. code-block:: python + + qprof.to_html("my-report.html") # Where "my-report.html" is the path of the file to save. \ No newline at end of file diff --git a/docs/source/whats_new_v1_0_2.rst b/docs/source/whats_new_v1_0_2.rst index 500850471..83daa87c3 100644 --- a/docs/source/whats_new_v1_0_2.rst +++ b/docs/source/whats_new_v1_0_2.rst @@ -34,7 +34,7 @@ Multi-TimeSeries (Beta) ----------------------- We added a new Time Series class: ``TimeSeriesByCategory``. This allows the users to build multiple models based off on a category. The number of models created -are equal to the categories. This saves users time to create multiple models separately. For more information please see :py:func:`~verticapy.machine_learning.vertica.tsa.ensemble.TimeSeriesByCategory`. +are equal to the categories. This saves users time to create multiple models separately. For more information please see :py:mod:`~verticapy.machine_learning.vertica.tsa.ensemble.TimeSeriesByCategory`. Plots ------ diff --git a/verticapy/__init__.py b/verticapy/__init__.py index 286105014..72764cf3c 100755 --- a/verticapy/__init__.py +++ b/verticapy/__init__.py @@ -38,8 +38,8 @@ __license__: str = "Apache License, Version 2.0" __version__: str = "1.1.0-beta" __iteration__: int = 1 -__date__: str = "10202024" -__last_commit__: str = "5b52704653170b07c6fa78145842f7504a951c93" +__date__: str = "10242024" +__last_commit__: str = "5a33b9a51250f3a7f9510840d5a3511ea31f855f" __long_version__: str = f"{__version__}-{__iteration__}—{__date__}-{__last_commit__}" __codecov__: float = 0.84 diff --git a/verticapy/core/parsers/csv.py b/verticapy/core/parsers/csv.py index 1bcbe9f0a..c1ded22e0 100755 --- a/verticapy/core/parsers/csv.py +++ b/verticapy/core/parsers/csv.py @@ -329,7 +329,7 @@ def read_csv( na_rep: Optional[str] = None, quotechar: str = '"', escape: str = "\027", - record_terminator: str = "\n", + record_terminator: Optional[str] = "\n", trim: bool = True, omit_empty_keys: bool = False, reject_on_duplicate: bool = False, @@ -720,6 +720,10 @@ def read_csv( dtype = format_type(dtype, dtype=dict) if isinstance(sep, NoneType): sep = "" + no_record = False + if isinstance(record_terminator, NoneType): + record_terminator = "\n" + no_record = True header_names = format_type(header_names, dtype=list) if schema: temporary_local_table = False @@ -917,13 +921,17 @@ def read_csv( local = "LOCAL " if ingest_local else "" header_names_str = ", ".join([f'"{column}"' for column in header_names]) record_terminator_quoted = _get_quoted_record_terminator(record_terminator) + if no_record: + record_terminator_line = "" + else: + record_terminator_line = f"RECORD TERMINATOR {record_terminator_quoted}" query2 = f""" COPY {input_relation}({header_names_str}) FROM {local}'{path}' {compression} DELIMITER '{sep}' NULL '{na_rep}' ENCLOSED BY '{quotechar}' - RECORD TERMINATOR {record_terminator_quoted} + {record_terminator_line} ESCAPE AS '{escape}'{skip};""" if genSQL: if insert: @@ -941,7 +949,18 @@ def read_csv( print_message( f"The table {input_relation} has been successfully created." ) - return vDataFrame(table_name, schema=schema) + res = vDataFrame(table_name, schema=schema) + nb_rows = res.shape()[0] + if nb_rows == 0 and not (no_record): + warning_message = ( + "The data ingestion might have failed as no rows" + " were ingested. The issue could be related to " + "the 'record_terminator' parameter. Setting it " + "to 'None' might resolve the problem.\n" + "Ex: read_csv(..., record_terminator=None, ...)" + ) + print_message(warning_message, "warning") + return res def _get_quoted_record_terminator(record_terminator: str) -> str: diff --git a/verticapy/core/vdataframe/_read.py b/verticapy/core/vdataframe/_read.py index 17b0a075d..7b63549e3 100755 --- a/verticapy/core/vdataframe/_read.py +++ b/verticapy/core/vdataframe/_read.py @@ -739,25 +739,26 @@ def select(self, columns: SQLColumns) -> "vDataFrame": | ``vDataFrame.``:py:meth:`~verticapy.vDataFrame.iloc` : Get custom rows from a :py:class:`~vDataFrame`. """ - all_cols = self.get_columns() columns = format_type(columns, dtype=list) for i in range(len(columns)): column = self.format_colnames(columns[i], raise_error=False) if column: - if column in all_cols: - dtype = self[column].ctype().lower() - else: - dtype = "" + dtype = "" if self._vars["isflex"]: + try: + dtype = self[column].ctype().lower() + except: + dtype = "" if ( "array" in dtype or "map" in dtype or "row" in dtype or "set" in dtype or "vmap" in dtype + or not (dtype) ): dtype = "" - elif column in all_cols: + else: dtype = f"::{dtype}" columns[i] = column + dtype else: diff --git a/verticapy/machine_learning/vertica/linear_model.py b/verticapy/machine_learning/vertica/linear_model.py index 29c0e414b..bd095c7b5 100755 --- a/verticapy/machine_learning/vertica/linear_model.py +++ b/verticapy/machine_learning/vertica/linear_model.py @@ -1138,8 +1138,8 @@ def _model_subcategory(self) -> Literal["REGRESSOR"]: return "REGRESSOR" @property - def _model_type(self) -> Literal["LinearRegression"]: - return "LinearRegression" + def _model_type(self) -> Literal["ElasticNet"]: + return "ElasticNet" # System & Special Methods. @@ -1673,8 +1673,8 @@ def _model_subcategory(self) -> Literal["REGRESSOR"]: return "REGRESSOR" @property - def _model_type(self) -> Literal["LinearRegression"]: - return "LinearRegression" + def _model_type(self) -> Literal["Lasso"]: + return "Lasso" # System & Special Methods. @@ -3982,8 +3982,8 @@ def _model_subcategory(self) -> Literal["REGRESSOR"]: return "REGRESSOR" @property - def _model_type(self) -> Literal["LinearRegression"]: - return "LinearRegression" + def _model_type(self) -> Literal["Ridge"]: + return "Ridge" # System & Special Methods.