diff --git a/.gitignore b/.gitignore index d932406..2a6da61 100644 --- a/.gitignore +++ b/.gitignore @@ -109,7 +109,7 @@ venv/ ENV/ env.bak/ venv.bak/ -.py310/ +.py31*/ # Spyder project settings .spyderproject diff --git a/CHANGES.rst b/CHANGES.rst index 4bdaab5..f049ceb 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -1,3 +1,29 @@ + +Release 1.4.0 +========================================= + +* **MAJOR** performance gains in the ``.to_js_literal()`` method. Implementation seems to + improve performance by 50 - 90%. +* *SIGNIFICANT* performance gains in the ``.to_json()`` method. Implementation seems to + improve performance by 30 - 90%. +* **ENHANCEMENT:** Significantly simplified use of the ``.from_pandas()`` method to support: + + * creation of multiple series from one DataFrame in one method call + * creation of series without needing to specify a full property map + * support for creating series by DataFrame row, rather than just by DataFrame column + +* **ENHANCEMENT:** Added the ``.from_pandas_in_rows()`` method to support creation of + charts and series from simple two-dimensional DataFrames laid out in rows. +* **ENHANCEMENT:** Added one-shot chart creation and rendering from Series objects. +* **ENHANCEMENT:** Added one-shot chart creation using ``series`` and ``data``/``series_type`` keywords. +* **ENHANCEMENT:** Added ``.convert_to()`` convenience method to Series objects. +* **ENHANCEMENT:** Added ``CallbackFunction.from_python()`` method which converts a Python function + to its JavaScript equivalent using generative AI, with support for both OpenAI and Anthropic. +* **BUGFIX:** Fixed instability issues in Jupyter Notebooks, both when operating as a Notebook (outside of + Jupyter Lab) and when saved to a static HTML file. + +--------------------- + Release 1.3.0 ========================================= diff --git a/README.rst b/README.rst index 2e60df8..27ee447 100644 --- a/README.rst +++ b/README.rst @@ -207,36 +207,35 @@ Hello World, and Basic Usage 1. Import Highcharts Gantt for Python ========================================== - .. code-block:: python - - # BEST PRACTICE! - # PRECISE LOCATION PATTERN - # This method of importing Highcharts Gantt for Python objects yields the fastest - # performance for the import statement. However, it is more verbose and requires - # you to navigate the extensive Highcharts Gantt for Python API. - - # Import classes using precise module indications. For example: - from highcharts_gantt.chart import Chart - from highcharts_gantt.global_options.shared_options import SharedGanttOptions - from highcharts_gantt.options import HighchartsGanttOptions - from highcharts_gantt.options.plot_options.gantt import GanttOptions - from highcharts_gantt.options.series.gantt import GanttSeries - - # CATCH-ALL PATTERN - # This method of importing Highcharts Gantt for Python classes has relatively slow - # performance because it imports hundreds of different classes from across the entire - # library. This is also a known anti-pattern, as it obscures the namespace within the - # library. Both may be acceptable to you in your use-case, but do use at your own risk. - - # Import objects from the catch-all ".highcharts" module. - from highcharts_gantt import highcharts - - # You can now access specific classes without individual import statements. - highcharts.Chart - highcharts.SharedGanttOptions - highcharts.HighchartsGanttOptions - highcharts.GanttOptions - highcharts.GanttSeries +.. code-block:: python + + # PRECISE-LOCATION PATTERN: BEST PRACTICE! + # This method of importing Highcharts for Python objects yields the fastest + # performance for the import statement. However, it is more verbose and requires + # you to navigate the extensive Highcharts Core for Python API. + + # Import classes using precise module indications. For example: + from highcharts_core.chart import Chart + from highcharts_core.global_options.shared_options import SharedOptions + from highcharts_core.options import HighchartsOptions + from highcharts_core.options.plot_options.bar import BarOptions + from highcharts_core.options.series.bar import BarSeries + + # CATCH-ALL PATTERN + # This method of importing Highcharts for Python classes has relatively slow + # performance because it imports hundreds of different classes from across the entire + # library. This performance impact may be acceptable to you in your use-case, but + # do use at your own risk. + + # Import objects from the catch-all ".highcharts" module. + from highcharts_core import highcharts + + # You can now access specific classes without individual import statements. + highcharts.Chart + highcharts.SharedOptions + highcharts.HighchartsOptions + highcharts.BarOptions + highcharts.BarSeries 2. Create Your Chart @@ -244,47 +243,52 @@ Hello World, and Basic Usage .. code-block:: python + # from a primitive array, using keyword arguments + my_chart = Chart(data = [[1, 23], [2, 34], [3, 45]], + series_type = 'line') + + # from a primitive array, using the .from_array() method + my_chart = Chart.from_array([[1, 23], [2, 34], [3, 45]], + series_type = 'line') + + # from a Numpy ndarray, using keyword arguments + my_chart = Chart(data = numpy_array, series_type = 'line') + + # from a Numpy ndarray, using the .from_array() method + my_chart = Chart.from_array(data = numpy_array, series_type = 'line') + # from a JavaScript file - my_chart = highcharts.Chart.from_js_literal('my_js_literal.js') + my_chart = Chart.from_js_literal('my_js_literal.js') # from a JSON file - my_chart = highcharts.Chart.from_json('my_json.json') + my_chart = Chart.from_json('my_json.json') # from a Python dict - my_chart = highcharts.Chart.from_dict(my_dict_obj) + my_chart = Chart.from_dict(my_dict_obj) # from a Pandas dataframe - my_chart = highcharts.Chart.from_pandas(df, - property_map = { - 'x': 'transactionDate', - 'y': 'invoiceAmt', - 'id': 'id' - }, - series_type = 'line') + my_chart = Chart.from_pandas(df) # from a PySpark dataframe - my_chart = highcharts.Chart.from_pyspark(df, - property_map = { - 'x': 'transactionDate', - 'y': 'invoiceAmt', - 'id': 'id' - }, - series_type = 'line') + my_chart = Chart.from_pyspark(df, + property_map = { + 'x': 'transactionDate', + 'y': 'invoiceAmt', + 'id': 'id' + }, + series_type = 'line') # from a CSV - my_chart = highcharts.Chart.from_csv('/some_file_location/filename.csv' - column_property_map = { - 'x': 0, - 'y': 4, - 'id': 14 - }, - series_type = 'line') + my_chart = Chart.from_csv('/some_file_location/filename.csv') # from a HighchartsOptions configuration object - my_chart = highcharts.Chart.from_options(my_options) + my_chart = Chart.from_options(my_options) + + # from a Series configuration, using keyword arguments + my_chart = Chart(series = my_series) - # from a Series configuration - my_chart = highcharts.Chart.from_series(my_series) + # from a Series configuration, using .from_series() + my_chart = Chart.from_series(my_series) 3. Configure Global Settings (optional) @@ -292,20 +296,20 @@ Hello World, and Basic Usage .. code-block:: python - # Import SharedGanttOptions - from highcharts_gantt.global_options.shared_options import SharedGanttOptions + # Import SharedOptions + from highcharts_core.global_options.shared_options import SharedOptions # from a JavaScript file - my_global_settings = SharedGanttOptions.from_js_literal('my_js_literal.js') + my_global_settings = SharedOptions.from_js_literal('my_js_literal.js') # from a JSON file - my_global_settings = SharedGanttOptions.from_json('my_json.json') + my_global_settings = SharedOptions.from_json('my_json.json') # from a Python dict - my_global_settings = SharedGanttOptions.from_dict(my_dict_obj) + my_global_settings = SharedOptions.from_dict(my_dict_obj) # from a HighchartsOptions configuration object - my_global_settings = SharedGanttOptions.from_options(my_options) + my_global_settings = SharedOptions.from_options(my_options) 4. Configure Your Chart / Global Settings @@ -313,9 +317,10 @@ Hello World, and Basic Usage .. code-block:: python - from highcharts_gantt.options.title import Title - from highcharts_gantt.options.credits import Credits + from highcharts_core.options.title import Title + from highcharts_core.options.credits import Credits + # EXAMPLE 1. # Using dicts my_chart.title = { 'align': 'center' @@ -326,7 +331,7 @@ Hello World, and Basic Usage my_chart.credits = { 'enabled': True, - 'href': 'https://www.highcharts.com/', + 'href': 'https://www.highchartspython.com/', 'position': { 'align': 'center', 'vertical_align': 'bottom', @@ -341,17 +346,21 @@ Hello World, and Basic Usage 'text': 'Chris Modzelewski' } + # EXAMPLE 2. # Using direct objects - from highcharts_gantt.options.title import Title - from highcharts_gantt.options.credits import Credits + from highcharts_core.options.title import Title + from highcharts_core.options.credits import Credits - my_title = Title(text = 'The Title for My Chart', floating = True, align = 'center') + my_title = Title(text = 'The Title for My Chart', + floating = True, + align = 'center') my_chart.options.title = my_title - my_credits = Credits(text = 'Chris Modzelewski', enabled = True, href = 'https://www.highcharts.com') + my_credits = Credits(text = 'Chris Modzelewski', + enabled = True, + href = 'https://www.highchartspython.com') my_chart.options.credits = my_credits - 5. Generate the JavaScript Code for Your Chart ================================================= @@ -360,9 +369,11 @@ that will render the chart wherever it is you want it to go: .. code-block:: python + # EXAMPLE 1. # as a string js_as_str = my_chart.to_js_literal() + # EXAMPLE 2. # to a file (and as a string) js_as_str = my_chart.to_js_literal(filename = 'my_target_file.js') @@ -391,6 +402,13 @@ that will render the chart wherever it is you want it to go: my_image_bytes = my_chart.download_chart(filename = 'my_target_file.png', format = 'png') +8. Render Your Chart in a Jupyter Notebook +=============================================== + + .. code-block:: python + + my_chart.display() + -------------- *********************** diff --git a/docs/_static/highcharts-chart-anatomy.png b/docs/_static/highcharts-chart-anatomy.png new file mode 100644 index 0000000..60ec37b Binary files /dev/null and b/docs/_static/highcharts-chart-anatomy.png differ diff --git a/docs/_static/tutorials/census-time-series-01.png b/docs/_static/tutorials/census-time-series-01.png new file mode 100644 index 0000000..8b0d308 Binary files /dev/null and b/docs/_static/tutorials/census-time-series-01.png differ diff --git a/docs/_static/tutorials/census-time-series-02.png b/docs/_static/tutorials/census-time-series-02.png new file mode 100644 index 0000000..19135f6 Binary files /dev/null and b/docs/_static/tutorials/census-time-series-02.png differ diff --git a/docs/_static/tutorials/census-time-series-03.png b/docs/_static/tutorials/census-time-series-03.png new file mode 100644 index 0000000..c268ac3 Binary files /dev/null and b/docs/_static/tutorials/census-time-series-03.png differ diff --git a/docs/_static/tutorials/census-time-series-04.png b/docs/_static/tutorials/census-time-series-04.png new file mode 100644 index 0000000..16ed17b Binary files /dev/null and b/docs/_static/tutorials/census-time-series-04.png differ diff --git a/docs/_static/tutorials/census-time-series-05.png b/docs/_static/tutorials/census-time-series-05.png new file mode 100644 index 0000000..98414fe Binary files /dev/null and b/docs/_static/tutorials/census-time-series-05.png differ diff --git a/docs/_static/tutorials/census-time-series-06.png b/docs/_static/tutorials/census-time-series-06.png new file mode 100644 index 0000000..07627cc Binary files /dev/null and b/docs/_static/tutorials/census-time-series-06.png differ diff --git a/docs/_static/tutorials/census-time-series-07.png b/docs/_static/tutorials/census-time-series-07.png new file mode 100644 index 0000000..73d7611 Binary files /dev/null and b/docs/_static/tutorials/census-time-series-07.png differ diff --git a/docs/_static/tutorials/census-time-series-08.png b/docs/_static/tutorials/census-time-series-08.png new file mode 100644 index 0000000..0a1ea2d Binary files /dev/null and b/docs/_static/tutorials/census-time-series-08.png differ diff --git a/docs/_static/tutorials/census-time-series-09.png b/docs/_static/tutorials/census-time-series-09.png new file mode 100644 index 0000000..90c222c Binary files /dev/null and b/docs/_static/tutorials/census-time-series-09.png differ diff --git a/docs/_static/tutorials/census-time-series-10.png b/docs/_static/tutorials/census-time-series-10.png new file mode 100644 index 0000000..49ce258 Binary files /dev/null and b/docs/_static/tutorials/census-time-series-10.png differ diff --git a/docs/_static/tutorials/census-time-series-csv-01.png b/docs/_static/tutorials/census-time-series-csv-01.png new file mode 100644 index 0000000..e444567 Binary files /dev/null and b/docs/_static/tutorials/census-time-series-csv-01.png differ diff --git a/docs/_static/tutorials/raw-data-as-numpy.png b/docs/_static/tutorials/raw-data-as-numpy.png new file mode 100644 index 0000000..542b285 Binary files /dev/null and b/docs/_static/tutorials/raw-data-as-numpy.png differ diff --git a/docs/api.rst b/docs/api.rst index fd23379..8baa434 100644 --- a/docs/api.rst +++ b/docs/api.rst @@ -651,63 +651,94 @@ Core Components - * - :mod:`.options.series.data.accessibility ` - :class:`DataPointAccessibility ` - * - :mod:`.options.series.data.arcdiagram ` - - :class:`ArcDiagramData ` - * - :mod:`.options.series.data.bar ` - - :class:`BarData ` - :class:`WaterfallData ` - :class:`WindBarbData ` - :class:`XRangeData ` - * - :mod:`.options.series.data.base ` - - :class:`DataBase ` - * - :mod:`.options.series.data.boxplot ` - - :class:`BoxPlotData ` - * - :mod:`.options.series.data.bullet ` - - :class:`BulletData ` - * - :mod:`.options.series.data.candlestick ` - - :class:`CandlestickData ` - * - :mod:`.options.series.data.cartesian ` - - :class:`CartesianData ` - :class:`Cartesian3DData ` - :class:`CartesianValueData ` - * - :mod:`.options.series.data.connect ` - - :class:`DataConnection ` - * - :mod:`.options.series.data.connections ` - - :class:`ConnectionData ` - :class:`WeightedConnectionData ` - :class:`OutgoingWeightedConnectionData ` - :class:`ConnectionBase ` + * - :mod:`.options.series.data.arcdiagram ` + - :class:`ArcDiagramData ` + :class:`ArcDiagramDataCollection ` + * - :mod:`.options.series.data.bar ` + - :class:`BarData ` + :class:`BarDataCollection ` + :class:`WaterfallData ` + :class:`WaterfallDataCollection ` + :class:`WindBarbData ` + :class:`WindBarbDataCollection ` + :class:`XRangeData ` + :class:`XRangeDataCollection ` + * - :mod:`.options.series.data.base ` + - :class:`DataBase ` + * - :mod:`.options.series.data.boxplot ` + - :class:`BoxPlotData ` + :class:`BoxPlotDataCollection ` + * - :mod:`.options.series.data.bullet ` + - :class:`BulletData ` + :class:`BulletDataCollection ` + * - :mod:`.options.series.data.candlestick ` + - :class:`CandlestickData ` + * - :mod:`.options.series.data.cartesian ` + - :class:`CartesianData ` + :class:`CartesianDataCollection ` + :class:`Cartesian3DData ` + :class:`Cartesian3DDataCollection ` + :class:`CartesianValueData ` + :class:`CartesianValueDataCollection ` + * - :mod:`.options.series.data.collections ` + - :class:`DataPointCollection ` + * - :mod:`.options.series.data.connections ` + - :class:`ConnectionData ` + :class:`ConnectionDataCollection ` + :class:`WeightedConnectionData ` + :class:`WeightedConnectionDataCollection ` + :class:`OutgoingWeightedConnectionData ` + :class:`OutgoingWeightedConnectionDataCollection ` + :class:`ConnectionBase ` * - :mod:`.options.series.data.gantt ` - :class:`GanttData ` + :class:`GanttDataCollection ` :class:`ProgressIndicator ` - * - :mod:`.options.series.data.hlc ` - - :class:`HLCData ` - :class:`OHLCData ` - * - :mod:`.options.series.data.pie ` - - :class:`PieData ` - :class:`VariablePieData ` - * - :mod:`.options.series.data.range ` - - :class:`RangeData ` - :class:`ConnectedRangeData ` - * - :mod:`.options.series.data.single_point ` - - :class:`SinglePointData ` - :class:`SingleValueData ` - :class:`SingleXData ` - :class:`LabeledSingleXData ` - :class:`ConnectedSingleXData ` - :class:`SinglePointBase ` - * - :mod:`.options.series.data.sunburst ` - - :class:`SunburstData ` - * - :mod:`.options.series.data.treegraph ` - - :class:`TreegraphData ` - * - :mod:`.options.series.data.treemap ` - - :class:`TreemapData ` - * - :mod:`.options.series.data.vector ` - - :class:`VectorData ` - * - :mod:`.options.series.data.venn ` - - :class:`VennData ` - * - :mod:`.options.series.data.wordcloud ` - - :class:`WordcloudData ` + * - :mod:`.options.series.data.hlc ` + - :class:`HLCData ` + :class:`HLCDataCollection ` + :class:`OHLCData ` + :class:`OHLCDataCollection ` + * - :mod:`.options.series.data.pie ` + - :class:`PieData ` + :class:`PieDataCollection ` + :class:`VariablePieData ` + :class:`VariablePieDataCollection ` + * - :mod:`.options.series.data.range ` + - :class:`RangeData ` + :class:`RangeDataCollection ` + :class:`ConnectedRangeData ` + :class:`ConnectedRangeDataCollection ` + * - :mod:`.options.series.data.single_point ` + - :class:`SinglePointData ` + :class:`SinglePointDataCollection ` + :class:`SingleValueData ` + :class:`SingleValueDataCollection ` + :class:`SingleXData ` + :class:`SingleXDataCollection ` + :class:`LabeledSingleXData ` + :class:`LabeledSingleXDataCollection ` + :class:`ConnectedSingleXData ` + :class:`ConnectedSingleXDataCollection ` + :class:`SinglePointBase ` + * - :mod:`.options.series.data.sunburst ` + - :class:`SunburstData ` + :class:`SunburstDataCollection ` + * - :mod:`.options.series.data.treegraph ` + - :class:`TreegraphData ` + :class:`TreegraphDataCollection ` + * - :mod:`.options.series.data.treemap ` + - :class:`TreemapData ` + :class:`TreemapDataCollection ` + * - :mod:`.options.series.data.vector ` + - :class:`VectorData ` + :class:`VectorDataCollection ` + * - :mod:`.options.series.data.venn ` + - :class:`VennData ` + :class:`VennDataCollection ` + * - :mod:`.options.series.data.wordcloud ` + - :class:`WordcloudData ` + :class:`WordcloudDataCollection ` * - :mod:`.options.series.dependencywheel ` - :class:`DependencyWheelSeries ` * - :mod:`.options.series.disparity_index ` diff --git a/docs/api/options/series/data/arcdiagram.rst b/docs/api/options/series/data/arcdiagram.rst index 081df0e..bc85b85 100644 --- a/docs/api/options/series/data/arcdiagram.rst +++ b/docs/api/options/series/data/arcdiagram.rst @@ -1,5 +1,5 @@ ########################################################################################## -:mod:`.arcdiagram ` +:mod:`.arcdiagram ` ########################################################################################## .. contents:: Module Contents @@ -9,10 +9,10 @@ -------------- -.. module:: highcharts_gantt.options.series.data.arcdiagram +.. module:: highcharts_core.options.series.data.arcdiagram ******************************************************************************************************************** -class: :class:`ArcDiagramData ` +class: :class:`ArcDiagramData ` ******************************************************************************************************************** .. autoclass:: ArcDiagramData @@ -22,7 +22,27 @@ class: :class:`ArcDiagramData ` +******************************************************************************************************************** + +.. autoclass:: ArcDiagramDataCollection + :members: + :inherited-members: + + .. collapse:: Class Inheritance + + .. inheritance-diagram:: ArcDiagramDataCollection + :top-classes: highcharts_core.metaclasses.HighchartsMeta :parts: -1 | diff --git a/docs/api/options/series/data/bar.rst b/docs/api/options/series/data/bar.rst index e682053..384812c 100644 --- a/docs/api/options/series/data/bar.rst +++ b/docs/api/options/series/data/bar.rst @@ -1,5 +1,5 @@ ########################################################################################## -:mod:`.bar ` +:mod:`.bar ` ########################################################################################## .. contents:: Module Contents @@ -9,10 +9,10 @@ -------------- -.. module:: highcharts_gantt.options.series.data.bar +.. module:: highcharts_core.options.series.data.bar ******************************************************************************************************************** -class: :class:`BarData ` +class: :class:`BarData ` ******************************************************************************************************************** .. autoclass:: BarData @@ -22,7 +22,25 @@ class: :class:`BarData ` .. collapse:: Class Inheritance .. inheritance-diagram:: BarData - :top-classes: highcharts_gantt.metaclasses.HighchartsMeta, highcharts_core.metaclasses.HighchartsMeta + :top-classes: highcharts_core.metaclasses.HighchartsMeta + :parts: -1 + + | + +-------------- + +******************************************************************************************************************** +class: :class:`BarDataCollection ` +******************************************************************************************************************** + +.. autoclass:: BarDataCollection + :members: + :inherited-members: + + .. collapse:: Class Inheritance + + .. inheritance-diagram:: BarDataCollection + :top-classes: highcharts_core.metaclasses.HighchartsMeta :parts: -1 | @@ -30,7 +48,7 @@ class: :class:`BarData ` ----------------- ******************************************************************************************************************** -class: :class:`WaterfallData ` +class: :class:`WaterfallData ` ******************************************************************************************************************** .. autoclass:: WaterfallData @@ -40,7 +58,25 @@ class: :class:`WaterfallData ` +******************************************************************************************************************** + +.. autoclass:: WaterfallDataCollection + :members: + :inherited-members: + + .. collapse:: Class Inheritance + + .. inheritance-diagram:: WaterfallDataCollection + :top-classes: highcharts_core.metaclasses.HighchartsMeta :parts: -1 | @@ -48,7 +84,7 @@ class: :class:`WaterfallData ` +class: :class:`WindBarbData ` ******************************************************************************************************************** .. autoclass:: WindBarbData @@ -58,7 +94,7 @@ class: :class:`WindBarbData ` +class: :class:`WindBarbDataCollection ` +******************************************************************************************************************** + +.. autoclass:: WindBarbDataCollection + :members: + :inherited-members: + + .. collapse:: Class Inheritance + + .. inheritance-diagram:: WindBarbDataCollection + :top-classes: highcharts_core.metaclasses.HighchartsMeta + :parts: -1 + + | + +----------------- + +******************************************************************************************************************** +class: :class:`XRangeData ` ******************************************************************************************************************** .. autoclass:: XRangeData @@ -76,7 +130,25 @@ class: :class:`XRangeData ` .. collapse:: Class Inheritance .. inheritance-diagram:: XRangeData - :top-classes: highcharts_gantt.metaclasses.HighchartsMeta, highcharts_core.metaclasses.HighchartsMeta + :top-classes: highcharts_core.metaclasses.HighchartsMeta + :parts: -1 + + | + +----------------- + +******************************************************************************************************************** +class: :class:`XRangeDataCollection ` +******************************************************************************************************************** + +.. autoclass:: XRangeDataCollection + :members: + :inherited-members: + + .. collapse:: Class Inheritance + + .. inheritance-diagram:: XRangeDataCollection + :top-classes: highcharts_core.metaclasses.HighchartsMeta :parts: -1 | diff --git a/docs/api/options/series/data/base.rst b/docs/api/options/series/data/base.rst index b4d93f7..1a2f9dd 100644 --- a/docs/api/options/series/data/base.rst +++ b/docs/api/options/series/data/base.rst @@ -1,5 +1,5 @@ ########################################################################################## -:mod:`.base ` +:mod:`.base ` ########################################################################################## .. contents:: Module Contents @@ -9,10 +9,10 @@ -------------- -.. module:: highcharts_gantt.options.series.data.base +.. module:: highcharts_core.options.series.data.base ******************************************************************************************************************** -class: :class:`DataBase ` +class: :class:`DataBase ` ******************************************************************************************************************** .. autoclass:: DataBase @@ -22,7 +22,7 @@ class: :class:`DataBase ` .. collapse:: Class Inheritance .. inheritance-diagram:: DataBase - :top-classes: highcharts_gantt.metaclasses.HighchartsMeta, highcharts_core.metaclasses.HighchartsMeta + :top-classes: highcharts_core.metaclasses.HighchartsMeta :parts: -1 | diff --git a/docs/api/options/series/data/boxplot.rst b/docs/api/options/series/data/boxplot.rst index d8be857..95b1380 100644 --- a/docs/api/options/series/data/boxplot.rst +++ b/docs/api/options/series/data/boxplot.rst @@ -1,5 +1,5 @@ ########################################################################################## -:mod:`.boxplot ` +:mod:`.boxplot ` ########################################################################################## .. contents:: Module Contents @@ -9,10 +9,10 @@ -------------- -.. module:: highcharts_gantt.options.series.data.boxplot +.. module:: highcharts_core.options.series.data.boxplot ******************************************************************************************************************** -class: :class:`BoxPlotData ` +class: :class:`BoxPlotData ` ******************************************************************************************************************** .. autoclass:: BoxPlotData @@ -22,7 +22,25 @@ class: :class:`BoxPlotData ` +******************************************************************************************************************** + +.. autoclass:: BoxPlotDataCollection + :members: + :inherited-members: + + .. collapse:: Class Inheritance + + .. inheritance-diagram:: BoxPlotDataCollection + :top-classes: highcharts_core.metaclasses.HighchartsMeta :parts: -1 | diff --git a/docs/api/options/series/data/bullet.rst b/docs/api/options/series/data/bullet.rst index a805013..2ae5488 100644 --- a/docs/api/options/series/data/bullet.rst +++ b/docs/api/options/series/data/bullet.rst @@ -1,5 +1,5 @@ ########################################################################################## -:mod:`.bullet ` +:mod:`.bullet ` ########################################################################################## .. contents:: Module Contents @@ -9,10 +9,10 @@ -------------- -.. module:: highcharts_gantt.options.series.data.bullet +.. module:: highcharts_core.options.series.data.bullet ******************************************************************************************************************** -class: :class:`BulletData ` +class: :class:`BulletData ` ******************************************************************************************************************** .. autoclass:: BulletData @@ -22,7 +22,25 @@ class: :class:`BulletData ` +******************************************************************************************************************** + +.. autoclass:: BulletDataCollection + :members: + :inherited-members: + + .. collapse:: Class Inheritance + + .. inheritance-diagram:: BulletDataCollection + :top-classes: highcharts_core.metaclasses.HighchartsMeta :parts: -1 | diff --git a/docs/api/options/series/data/candlestick.rst b/docs/api/options/series/data/candlestick.rst index 09c1356..3a0eeac 100644 --- a/docs/api/options/series/data/candlestick.rst +++ b/docs/api/options/series/data/candlestick.rst @@ -1,5 +1,5 @@ ########################################################################################## -:mod:`.candlestick ` +:mod:`.candlestick ` ########################################################################################## .. contents:: Module Contents @@ -9,10 +9,10 @@ -------------- -.. module:: highcharts_gantt.options.series.data.candlestick +.. module:: highcharts_stock.options.series.data.candlestick ******************************************************************************************************************** -class: :class:`CandlestickData ` +class: :class:`CandlestickData ` ******************************************************************************************************************** .. autoclass:: CandlestickData @@ -22,7 +22,27 @@ class: :class:`CandlestickData ` +************************************************************************************************************************ + +.. autoclass:: CandlestickDataCollection + :members: + :inherited-members: + + .. collapse:: Class Inheritance + + .. inheritance-diagram:: CandlestickDataCollection + :top-classes: highcharts_stock.metaclasses.HighchartsMeta, highcharts_core.metaclasses.HighchartsMeta :parts: -1 | diff --git a/docs/api/options/series/data/cartesian.rst b/docs/api/options/series/data/cartesian.rst index af9fe8b..6c224de 100644 --- a/docs/api/options/series/data/cartesian.rst +++ b/docs/api/options/series/data/cartesian.rst @@ -1,5 +1,5 @@ ########################################################################################## -:mod:`.cartesian ` +:mod:`.cartesian ` ########################################################################################## .. contents:: Module Contents @@ -9,10 +9,10 @@ -------------- -.. module:: highcharts_gantt.options.series.data.cartesian +.. module:: highcharts_core.options.series.data.cartesian ******************************************************************************************************************** -class: :class:`CartesianData ` +class: :class:`CartesianData ` ******************************************************************************************************************** .. autoclass:: CartesianData @@ -22,7 +22,7 @@ class: :class:`CartesianData ` +class: :class:`CartesianDataCollection ` +******************************************************************************************************************** + +.. autoclass:: CartesianDataCollection + :members: + :inherited-members: + + .. collapse:: Class Inheritance + + .. inheritance-diagram:: CartesianDataCollection + :top-classes: highcharts_core.metaclasses.HighchartsMeta + :parts: -1 + + | + +-------------- + +******************************************************************************************************************** +class: :class:`Cartesian3DData ` ******************************************************************************************************************** .. autoclass:: Cartesian3DData @@ -40,7 +58,25 @@ class: :class:`Cartesian3DData ` +******************************************************************************************************************** + +.. autoclass:: Cartesian3DDataCollection + :members: + :inherited-members: + + .. collapse:: Class Inheritance + + .. inheritance-diagram:: Cartesian3DDataCollection + :top-classes: highcharts_core.metaclasses.HighchartsMeta :parts: -1 | @@ -48,7 +84,7 @@ class: :class:`Cartesian3DData ` +class: :class:`CartesianValueData ` ******************************************************************************************************************** .. autoclass:: CartesianValueData @@ -58,7 +94,25 @@ class: :class:`CartesianValueData ` +******************************************************************************************************************** + +.. autoclass:: CartesianValueDataCollection + :members: + :inherited-members: + + .. collapse:: Class Inheritance + + .. inheritance-diagram:: CartesianValueDataCollection + :top-classes: highcharts_core.metaclasses.HighchartsMeta :parts: -1 | diff --git a/docs/api/options/series/data/collections.rst b/docs/api/options/series/data/collections.rst new file mode 100644 index 0000000..748505a --- /dev/null +++ b/docs/api/options/series/data/collections.rst @@ -0,0 +1,28 @@ +########################################################################################## +:mod:`.collections ` +########################################################################################## + +.. contents:: Module Contents + :local: + :depth: 3 + :backlinks: entry + +-------------- + +.. module:: highcharts_core.options.series.data.collections + +******************************************************************************************************************** +class: :class:`DataPointCollection ` +******************************************************************************************************************** + +.. autoclass:: DataPointCollection + :members: + :inherited-members: + + .. collapse:: Class Inheritance + + .. inheritance-diagram:: DataPointCollection + :top-classes: highcharts_core.metaclasses.HighchartsMeta + :parts: -1 + + | diff --git a/docs/api/options/series/data/connections.rst b/docs/api/options/series/data/connections.rst index 2889111..fa4d2c9 100644 --- a/docs/api/options/series/data/connections.rst +++ b/docs/api/options/series/data/connections.rst @@ -1,5 +1,5 @@ ########################################################################################## -:mod:`.connections ` +:mod:`.connections ` ########################################################################################## .. contents:: Module Contents @@ -9,10 +9,10 @@ -------------- -.. module:: highcharts_gantt.options.series.data.connections +.. module:: highcharts_core.options.series.data.connections ******************************************************************************************************************** -class: :class:`ConnectionData ` +class: :class:`ConnectionData ` ******************************************************************************************************************** .. autoclass:: ConnectionData @@ -22,7 +22,7 @@ class: :class:`ConnectionData ` +class: :class:`ConnectionDataCollection ` +******************************************************************************************************************** + +.. autoclass:: ConnectionDataCollection + :members: + :inherited-members: + + .. collapse:: Class Inheritance + + .. inheritance-diagram:: ConnectionDataCollection + :top-classes: highcharts_core.metaclasses.HighchartsMeta + :parts: -1 + + | + +--------------- + +******************************************************************************************************************** +class: :class:`WeightedConnectionData ` ******************************************************************************************************************** .. autoclass:: WeightedConnectionData @@ -40,7 +58,25 @@ class: :class:`WeightedConnectionData ` +******************************************************************************************************************** + +.. autoclass:: WeightedConnectionDataCollection + :members: + :inherited-members: + + .. collapse:: Class Inheritance + + .. inheritance-diagram:: WeightedConnectionDataCollection + :top-classes: highcharts_core.metaclasses.HighchartsMeta :parts: -1 | @@ -48,7 +84,7 @@ class: :class:`WeightedConnectionData ` +class: :class:`OutgoingWeightedConnectionData ` ************************************************************************************************************************************* .. autoclass:: OutgoingWeightedConnectionData @@ -58,7 +94,25 @@ class: :class:`OutgoingWeightedConnectionData ` +************************************************************************************************************************************* + +.. autoclass:: OutgoingWeightedConnectionDataCollection + :members: + :inherited-members: + + .. collapse:: Class Inheritance + + .. inheritance-diagram:: OutgoingWeightedConnectionDataCollection + :top-classes: highcharts_core.metaclasses.HighchartsMeta :parts: -1 | @@ -66,7 +120,7 @@ class: :class:`OutgoingWeightedConnectionData ` +class: :class:`ConnectionBase ` ******************************************************************************************************************** .. autoclass:: ConnectionBase @@ -76,7 +130,7 @@ class: :class:`ConnectionBase ` -------------- +******************************************************************************************************************** +class: :class:`GanttDataCollection ` +******************************************************************************************************************** + +.. autoclass:: GanttDataCollection + :members: + :inherited-members: + + .. collapse:: Class Inheritance + + .. inheritance-diagram:: GanttDataCollection + :top-classes: highcharts_gantt.metaclasses.HighchartsMeta, highcharts_core.metaclasses.HighchartsMeta + :parts: -1 + + | + +-------------- + ******************************************************************************************************************** class: :class:`ProgressIndicator ` ******************************************************************************************************************** diff --git a/docs/api/options/series/data/hlc.rst b/docs/api/options/series/data/hlc.rst index d7f39ce..4127e23 100644 --- a/docs/api/options/series/data/hlc.rst +++ b/docs/api/options/series/data/hlc.rst @@ -1,5 +1,5 @@ ########################################################################################## -:mod:`.hlc ` +:mod:`.hlc ` ########################################################################################## .. contents:: Module Contents @@ -9,10 +9,10 @@ -------------- -.. module:: highcharts_gantt.options.series.data.hlc +.. module:: highcharts_stock.options.series.data.hlc ******************************************************************************************************************** -class: :class:`HLCData ` +class: :class:`HLCData ` ******************************************************************************************************************** .. autoclass:: HLCData @@ -22,7 +22,27 @@ class: :class:`HLCData ` .. collapse:: Class Inheritance .. inheritance-diagram:: HLCData - :top-classes: highcharts_gantt.metaclasses.HighchartsMeta, highcharts_core.metaclasses.HighchartsMeta + :top-classes: highcharts_stock.metaclasses.HighchartsMeta, highcharts_core.metaclasses.HighchartsMeta + :parts: -1 + + | + +-------------- + +.. module:: highcharts_stock.options.series.data.hlc + +******************************************************************************************************************** +class: :class:`HLCDataCollection ` +******************************************************************************************************************** + +.. autoclass:: HLCDataCollection + :members: + :inherited-members: + + .. collapse:: Class Inheritance + + .. inheritance-diagram:: HLCDataCollection + :top-classes: highcharts_stock.metaclasses.HighchartsMeta, highcharts_core.metaclasses.HighchartsMeta :parts: -1 | @@ -30,7 +50,7 @@ class: :class:`HLCData ` ----------------- ******************************************************************************************************************** -class: :class:`OHLCData ` +class: :class:`OHLCData ` ******************************************************************************************************************** .. autoclass:: OHLCData @@ -40,7 +60,25 @@ class: :class:`OHLCData ` .. collapse:: Class Inheritance .. inheritance-diagram:: OHLCData - :top-classes: highcharts_gantt.metaclasses.HighchartsMeta, highcharts_core.metaclasses.HighchartsMeta + :top-classes: highcharts_stock.metaclasses.HighchartsMeta, highcharts_core.metaclasses.HighchartsMeta + :parts: -1 + + | + +----------------- + +******************************************************************************************************************** +class: :class:`OHLCDataCollection ` +******************************************************************************************************************** + +.. autoclass:: OHLCDataCollection + :members: + :inherited-members: + + .. collapse:: Class Inheritance + + .. inheritance-diagram:: OHLCDataCollection + :top-classes: highcharts_stock.metaclasses.HighchartsMeta, highcharts_core.metaclasses.HighchartsMeta :parts: -1 | diff --git a/docs/api/options/series/data/index.rst b/docs/api/options/series/data/index.rst index 3f596bb..51faf5d 100644 --- a/docs/api/options/series/data/index.rst +++ b/docs/api/options/series/data/index.rst @@ -48,60 +48,91 @@ Sub-components - * - :mod:`.options.series.data.accessibility ` - :class:`DataPointAccessibility ` - * - :mod:`.options.series.data.arcdiagram ` - - :class:`ArcDiagramData ` - * - :mod:`.options.series.data.bar ` - - :class:`BarData ` - :class:`WaterfallData ` - :class:`WindBarbData ` - :class:`XRangeData ` - * - :mod:`.options.series.data.base ` - - :class:`DataBase ` - * - :mod:`.options.series.data.boxplot ` - - :class:`BoxPlotData ` - * - :mod:`.options.series.data.bullet ` - - :class:`BulletData ` - * - :mod:`.options.series.data.candlestick ` - - :class:`CandlestickData ` - * - :mod:`.options.series.data.cartesian ` - - :class:`CartesianData ` - :class:`Cartesian3DData ` - :class:`CartesianValueData ` - * - :mod:`.options.series.data.connect ` - - :class:`DataConnection ` - * - :mod:`.options.series.data.connections ` - - :class:`ConnectionData ` - :class:`WeightedConnectionData ` - :class:`OutgoingWeightedConnectionData ` - :class:`ConnectionBase ` + * - :mod:`.options.series.data.arcdiagram ` + - :class:`ArcDiagramData ` + :class:`ArcDiagramDataCollection ` + * - :mod:`.options.series.data.bar ` + - :class:`BarData ` + :class:`BarDataCollection ` + :class:`WaterfallData ` + :class:`WaterfallDataCollection ` + :class:`WindBarbData ` + :class:`WindBarbDataCollection ` + :class:`XRangeData ` + :class:`XRangeDataCollection ` + * - :mod:`.options.series.data.base ` + - :class:`DataBase ` + * - :mod:`.options.series.data.boxplot ` + - :class:`BoxPlotData ` + :class:`BoxPlotDataCollection ` + * - :mod:`.options.series.data.bullet ` + - :class:`BulletData ` + :class:`BulletDataCollection ` + * - :mod:`.options.series.data.candlestick ` + - :class:`CandlestickData ` + * - :mod:`.options.series.data.cartesian ` + - :class:`CartesianData ` + :class:`CartesianDataCollection ` + :class:`Cartesian3DData ` + :class:`Cartesian3DDataCollection ` + :class:`CartesianValueData ` + :class:`CartesianValueDataCollection ` + * - :mod:`.options.series.data.collections ` + - :class:`DataPointCollection ` + * - :mod:`.options.series.data.connections ` + - :class:`ConnectionData ` + :class:`ConnectionDataCollection ` + :class:`WeightedConnectionData ` + :class:`WeightedConnectionDataCollection ` + :class:`OutgoingWeightedConnectionData ` + :class:`OutgoingWeightedConnectionDataCollection ` + :class:`ConnectionBase ` * - :mod:`.options.series.data.gantt ` - :class:`GanttData ` + :class:`GanttDataCollection ` :class:`ProgressIndicator ` - * - :mod:`.options.series.data.hlc ` - - :class:`HLCData ` - :class:`OHLCData ` - * - :mod:`.options.series.data.pie ` - - :class:`PieData ` - :class:`VariablePieData ` - * - :mod:`.options.series.data.range ` - - :class:`RangeData ` - :class:`ConnectedRangeData ` - * - :mod:`.options.series.data.single_point ` - - :class:`SinglePointData ` - :class:`SingleValueData ` - :class:`SingleXData ` - :class:`LabeledSingleXData ` - :class:`ConnectedSingleXData ` - :class:`SinglePointBase ` - * - :mod:`.options.series.data.sunburst ` - - :class:`SunburstData ` - * - :mod:`.options.series.data.treegraph ` - - :class:`TreegraphData ` - * - :mod:`.options.series.data.treemap ` - - :class:`TreemapData ` - * - :mod:`.options.series.data.vector ` - - :class:`VectorData ` - * - :mod:`.options.series.data.venn ` - - :class:`VennData ` - * - :mod:`.options.series.data.wordcloud ` - - :class:`WordcloudData ` + * - :mod:`.options.series.data.hlc ` + - :class:`HLCData ` + :class:`HLCDataCollection ` + :class:`OHLCData ` + :class:`OHLCDataCollection ` + * - :mod:`.options.series.data.pie ` + - :class:`PieData ` + :class:`PieDataCollection ` + :class:`VariablePieData ` + :class:`VariablePieDataCollection ` + * - :mod:`.options.series.data.range ` + - :class:`RangeData ` + :class:`RangeDataCollection ` + :class:`ConnectedRangeData ` + :class:`ConnectedRangeDataCollection ` + * - :mod:`.options.series.data.single_point ` + - :class:`SinglePointData ` + :class:`SinglePointDataCollection ` + :class:`SingleValueData ` + :class:`SingleValueDataCollection ` + :class:`SingleXData ` + :class:`SingleXDataCollection ` + :class:`LabeledSingleXData ` + :class:`LabeledSingleXDataCollection ` + :class:`ConnectedSingleXData ` + :class:`ConnectedSingleXDataCollection ` + :class:`SinglePointBase ` + * - :mod:`.options.series.data.sunburst ` + - :class:`SunburstData ` + :class:`SunburstDataCollection ` + * - :mod:`.options.series.data.treegraph ` + - :class:`TreegraphData ` + :class:`TreegraphDataCollection ` + * - :mod:`.options.series.data.treemap ` + - :class:`TreemapData ` + :class:`TreemapDataCollection ` + * - :mod:`.options.series.data.vector ` + - :class:`VectorData ` + :class:`VectorDataCollection ` + * - :mod:`.options.series.data.venn ` + - :class:`VennData ` + :class:`VennDataCollection ` + * - :mod:`.options.series.data.wordcloud ` + - :class:`WordcloudData ` + :class:`WordcloudDataCollection ` diff --git a/docs/api/options/series/data/pie.rst b/docs/api/options/series/data/pie.rst index 0388319..6de63aa 100644 --- a/docs/api/options/series/data/pie.rst +++ b/docs/api/options/series/data/pie.rst @@ -1,5 +1,5 @@ ########################################################################################## -:mod:`.pie ` +:mod:`.pie ` ########################################################################################## .. contents:: Module Contents @@ -9,10 +9,10 @@ -------------- -.. module:: highcharts_gantt.options.series.data.pie +.. module:: highcharts_core.options.series.data.pie ******************************************************************************************************************** -class: :class:`PieData ` +class: :class:`PieData ` ******************************************************************************************************************** .. autoclass:: PieData @@ -22,7 +22,7 @@ class: :class:`PieData ` .. collapse:: Class Inheritance .. inheritance-diagram:: PieData - :top-classes: highcharts_gantt.metaclasses.HighchartsMeta, highcharts_core.metaclasses.HighchartsMeta + :top-classes: highcharts_core.metaclasses.HighchartsMeta :parts: -1 | @@ -30,7 +30,25 @@ class: :class:`PieData ` ----------------- ******************************************************************************************************************** -class: :class:`VariablePieData ` +class: :class:`PieDataCollection ` +******************************************************************************************************************** + +.. autoclass:: PieDataCollection + :members: + :inherited-members: + + .. collapse:: Class Inheritance + + .. inheritance-diagram:: PieDataCollection + :top-classes: highcharts_core.metaclasses.HighchartsMeta + :parts: -1 + + | + +----------------- + +******************************************************************************************************************** +class: :class:`VariablePieData ` ******************************************************************************************************************** .. autoclass:: VariablePieData @@ -40,7 +58,25 @@ class: :class:`VariablePieData ` +******************************************************************************************************************** + +.. autoclass:: VariablePieDataCollection + :members: + :inherited-members: + + .. collapse:: Class Inheritance + + .. inheritance-diagram:: VariablePieDataCollection + :top-classes: highcharts_core.metaclasses.HighchartsMeta :parts: -1 | diff --git a/docs/api/options/series/data/range.rst b/docs/api/options/series/data/range.rst index 3a232cd..7e0b98a 100644 --- a/docs/api/options/series/data/range.rst +++ b/docs/api/options/series/data/range.rst @@ -1,5 +1,5 @@ ########################################################################################## -:mod:`.range ` +:mod:`.range ` ########################################################################################## .. contents:: Module Contents @@ -9,10 +9,10 @@ -------------- -.. module:: highcharts_gantt.options.series.data.range +.. module:: highcharts_core.options.series.data.range ******************************************************************************************************************** -class: :class:`RangeData ` +class: :class:`RangeData ` ******************************************************************************************************************** .. autoclass:: RangeData @@ -22,7 +22,7 @@ class: :class:`RangeData ` .. collapse:: Class Inheritance .. inheritance-diagram:: RangeData - :top-classes: highcharts_gantt.metaclasses.HighchartsMeta, highcharts_core.metaclasses.HighchartsMeta + :top-classes: highcharts_core.metaclasses.HighchartsMeta :parts: -1 | @@ -30,7 +30,25 @@ class: :class:`RangeData ` --------------- ******************************************************************************************************************** -class: :class:`ConnectedRangeData ` +class: :class:`RangeDataCollection ` +******************************************************************************************************************** + +.. autoclass:: RangeDataCollection + :members: + :inherited-members: + + .. collapse:: Class Inheritance + + .. inheritance-diagram:: RangeDataCollection + :top-classes: highcharts_core.metaclasses.HighchartsMeta + :parts: -1 + + | + +--------------- + +******************************************************************************************************************** +class: :class:`ConnectedRangeData ` ******************************************************************************************************************** .. autoclass:: ConnectedRangeData @@ -40,7 +58,25 @@ class: :class:`ConnectedRangeData ` +*********************************************************************************************************************** + +.. autoclass:: ConnectedRangeDataCollection + :members: + :inherited-members: + + .. collapse:: Class Inheritance + + .. inheritance-diagram:: ConnectedRangeDataCollection + :top-classes: highcharts_core.metaclasses.HighchartsMeta :parts: -1 | diff --git a/docs/api/options/series/data/single_point.rst b/docs/api/options/series/data/single_point.rst index b3147c5..c4a8552 100644 --- a/docs/api/options/series/data/single_point.rst +++ b/docs/api/options/series/data/single_point.rst @@ -1,5 +1,5 @@ ########################################################################################## -:mod:`.single_point ` +:mod:`.single_point ` ########################################################################################## .. contents:: Module Contents @@ -9,10 +9,10 @@ -------------- -.. module:: highcharts_gantt.options.series.data.single_point +.. module:: highcharts_core.options.series.data.single_point ******************************************************************************************************************** -class: :class:`SinglePointData ` +class: :class:`SinglePointData ` ******************************************************************************************************************** .. autoclass:: SinglePointData @@ -22,7 +22,25 @@ class: :class:`SinglePointData ` +************************************************************************************************************************ + +.. autoclass:: SinglePointDataCollection + :members: + :inherited-members: + + .. collapse:: Class Inheritance + + .. inheritance-diagram:: SinglePointDataCollection + :top-classes: highcharts_core.metaclasses.HighchartsMeta :parts: -1 | @@ -30,7 +48,7 @@ class: :class:`SinglePointData ` +class: :class:`SingleValueData ` ******************************************************************************************************************** .. autoclass:: SingleValueData @@ -40,7 +58,25 @@ class: :class:`SingleValueData ` +*********************************************************************************************************************** + +.. autoclass:: SingleValueDataCollection + :members: + :inherited-members: + + .. collapse:: Class Inheritance + + .. inheritance-diagram:: SingleValueDataCollection + :top-classes: highcharts_core.metaclasses.HighchartsMeta :parts: -1 | @@ -48,7 +84,7 @@ class: :class:`SingleValueData ` +class: :class:`SingleXData ` ******************************************************************************************************************** .. autoclass:: SingleXData @@ -58,7 +94,25 @@ class: :class:`SingleXData ` +******************************************************************************************************************** + +.. autoclass:: SingleXDataCollection + :members: + :inherited-members: + + .. collapse:: Class Inheritance + + .. inheritance-diagram:: SingleXDataCollection + :top-classes: highcharts_core.metaclasses.HighchartsMeta :parts: -1 | @@ -66,7 +120,7 @@ class: :class:`SingleXData ` +class: :class:`LabeledSingleXData ` ******************************************************************************************************************** .. autoclass:: LabeledSingleXData @@ -76,7 +130,7 @@ class: :class:`LabeledSingleXData ` +class: :class:`LabeledSingleXDataCollection ` +******************************************************************************************************************** + +.. autoclass:: LabeledSingleXDataCollection + :members: + :inherited-members: + + .. collapse:: Class Inheritance + + .. inheritance-diagram:: LabeledSingleXDataCollection + :top-classes: highcharts_core.metaclasses.HighchartsMeta + :parts: -1 + + | + +--------------------- + +******************************************************************************************************************** +class: :class:`ConnectedSingleXData ` ******************************************************************************************************************** .. autoclass:: ConnectedSingleXData @@ -94,7 +166,25 @@ class: :class:`ConnectedSingleXData ` +******************************************************************************************************************** + +.. autoclass:: ConnectedSingleXDataCollection + :members: + :inherited-members: + + .. collapse:: Class Inheritance + + .. inheritance-diagram:: ConnectedSingleXDataCollection + :top-classes: highcharts_core.metaclasses.HighchartsMeta :parts: -1 | @@ -102,7 +192,7 @@ class: :class:`ConnectedSingleXData ` +class: :class:`SinglePointBase ` ******************************************************************************************************************** .. autoclass:: SinglePointBase @@ -112,7 +202,7 @@ class: :class:`SinglePointBase ` +:mod:`.sunburst ` ########################################################################################## .. contents:: Module Contents @@ -9,10 +9,10 @@ -------------- -.. module:: highcharts_gantt.options.series.data.sunburst +.. module:: highcharts_core.options.series.data.sunburst ******************************************************************************************************************** -class: :class:`SunburstData ` +class: :class:`SunburstData ` ******************************************************************************************************************** .. autoclass:: SunburstData @@ -22,7 +22,25 @@ class: :class:`SunburstData ` +******************************************************************************************************************** + +.. autoclass:: SunburstDataCollection + :members: + :inherited-members: + + .. collapse:: Class Inheritance + + .. inheritance-diagram:: SunburstDataCollection + :top-classes: highcharts_core.metaclasses.HighchartsMeta :parts: -1 | diff --git a/docs/api/options/series/data/treegraph.rst b/docs/api/options/series/data/treegraph.rst index e52148e..338c619 100644 --- a/docs/api/options/series/data/treegraph.rst +++ b/docs/api/options/series/data/treegraph.rst @@ -1,5 +1,5 @@ ########################################################################################## -:mod:`.treegraph ` +:mod:`.treegraph ` ########################################################################################## .. contents:: Module Contents @@ -9,10 +9,10 @@ -------------- -.. module:: highcharts_gantt.options.series.data.treegraph +.. module:: highcharts_core.options.series.data.treegraph ******************************************************************************************************************** -class: :class:`TreegraphData ` +class: :class:`TreegraphData ` ******************************************************************************************************************** .. autoclass:: TreegraphData @@ -22,7 +22,25 @@ class: :class:`TreegraphData ` +******************************************************************************************************************** + +.. autoclass:: TreegraphDataCollection + :members: + :inherited-members: + + .. collapse:: Class Inheritance + + .. inheritance-diagram:: TreegraphDataCollection + :top-classes: highcharts_core.metaclasses.HighchartsMeta :parts: -1 | diff --git a/docs/api/options/series/data/treemap.rst b/docs/api/options/series/data/treemap.rst index c03bb41..db5e169 100644 --- a/docs/api/options/series/data/treemap.rst +++ b/docs/api/options/series/data/treemap.rst @@ -1,5 +1,5 @@ ########################################################################################## -:mod:`.treemap ` +:mod:`.treemap ` ########################################################################################## .. contents:: Module Contents @@ -9,10 +9,10 @@ -------------- -.. module:: highcharts_gantt.options.series.data.treemap +.. module:: highcharts_core.options.series.data.treemap ******************************************************************************************************************** -class: :class:`TreemapData ` +class: :class:`TreemapData ` ******************************************************************************************************************** .. autoclass:: TreemapData @@ -22,7 +22,25 @@ class: :class:`TreemapData ` +******************************************************************************************************************** + +.. autoclass:: TreemapDataCollection + :members: + :inherited-members: + + .. collapse:: Class Inheritance + + .. inheritance-diagram:: TreemapDataCollection + :top-classes: highcharts_core.metaclasses.HighchartsMeta :parts: -1 | diff --git a/docs/api/options/series/data/vector.rst b/docs/api/options/series/data/vector.rst index 8d83b58..91c63b8 100644 --- a/docs/api/options/series/data/vector.rst +++ b/docs/api/options/series/data/vector.rst @@ -1,5 +1,5 @@ ########################################################################################## -:mod:`.vector ` +:mod:`.vector ` ########################################################################################## .. contents:: Module Contents @@ -9,10 +9,10 @@ -------------- -.. module:: highcharts_gantt.options.series.data.vector +.. module:: highcharts_core.options.series.data.vector ******************************************************************************************************************** -class: :class:`VectorData ` +class: :class:`VectorData ` ******************************************************************************************************************** .. autoclass:: VectorData @@ -22,7 +22,25 @@ class: :class:`VectorData ` +******************************************************************************************************************** + +.. autoclass:: VectorDataCollection + :members: + :inherited-members: + + .. collapse:: Class Inheritance + + .. inheritance-diagram:: VectorDataCollection + :top-classes: highcharts_core.metaclasses.HighchartsMeta :parts: -1 | diff --git a/docs/api/options/series/data/venn.rst b/docs/api/options/series/data/venn.rst index 477a7de..3b4f3c5 100644 --- a/docs/api/options/series/data/venn.rst +++ b/docs/api/options/series/data/venn.rst @@ -1,5 +1,5 @@ ########################################################################################## -:mod:`.venn ` +:mod:`.venn ` ########################################################################################## .. contents:: Module Contents @@ -9,10 +9,10 @@ -------------- -.. module:: highcharts_gantt.options.series.data.venn +.. module:: highcharts_core.options.series.data.venn ******************************************************************************************************************** -class: :class:`VennData ` +class: :class:`VennData ` ******************************************************************************************************************** .. autoclass:: VennData @@ -22,7 +22,25 @@ class: :class:`VennData ` .. collapse:: Class Inheritance .. inheritance-diagram:: VennData - :top-classes: highcharts_gantt.metaclasses.HighchartsMeta, highcharts_core.metaclasses.HighchartsMeta + :top-classes: highcharts_core.metaclasses.HighchartsMeta + :parts: -1 + + | + +-------------- + +******************************************************************************************************************** +class: :class:`VennDataCollection ` +******************************************************************************************************************** + +.. autoclass:: VennDataCollection + :members: + :inherited-members: + + .. collapse:: Class Inheritance + + .. inheritance-diagram:: VennDataCollection + :top-classes: highcharts_core.metaclasses.HighchartsMeta :parts: -1 | diff --git a/docs/api/options/series/data/wordcloud.rst b/docs/api/options/series/data/wordcloud.rst index 633619b..f4989e1 100644 --- a/docs/api/options/series/data/wordcloud.rst +++ b/docs/api/options/series/data/wordcloud.rst @@ -1,5 +1,5 @@ ########################################################################################## -:mod:`.wordcloud ` +:mod:`.wordcloud ` ########################################################################################## .. contents:: Module Contents @@ -9,10 +9,10 @@ -------------- -.. module:: highcharts_gantt.options.series.data.wordcloud +.. module:: highcharts_core.options.series.data.wordcloud ******************************************************************************************************************** -class: :class:`WordcloudData ` +class: :class:`WordcloudData ` ******************************************************************************************************************** .. autoclass:: WordcloudData @@ -22,7 +22,25 @@ class: :class:`WordcloudData ` +******************************************************************************************************************** + +.. autoclass:: WordcloudDataCollection + :members: + :inherited-members: + + .. collapse:: Class Inheritance + + .. inheritance-diagram:: WordcloudDataCollection + :top-classes: highcharts_core.metaclasses.HighchartsMeta :parts: -1 | diff --git a/docs/index.rst b/docs/index.rst index 7aba459..a8f3d5e 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -13,8 +13,6 @@ Highcharts Gantt for Python **High-end Gantt chart visualization for the Python ecosystem** -.. include:: _unit_tests_code_coverage.rst - .. toctree:: :hidden: :maxdepth: 3 @@ -27,6 +25,7 @@ Highcharts Gantt for Python FAQ Toolkit Components and Roadmap Using Highcharts Gantt for Python + Tutorials API Reference Error Reference Getting Help @@ -48,6 +47,7 @@ Highcharts Gantt for Python * Highcharts Core for Python 1.0 or higher * Jupyter Notebook 6.4 or higher * IPython 8.10 or higher + * NumPy 1.19 or higher, * Pandas 1.3 or higher * PySpark 3.3 or higher * Asana 3.0 or higher @@ -247,77 +247,72 @@ Hello World, and Basic Usage .. code-block:: python - # from a JavaScript file - my_chart = highcharts.Chart.from_js_literal('my_js_literal.js') + # from a primitive array, using keyword arguments + my_chart = Chart(data = [[1, 23], [2, 34], [3, 45]], + series_type = 'line') - # from a JSON file - my_chart = highcharts.Chart.from_json('my_json.json') + # from a primitive array, using the .from_array() method + my_chart = Chart.from_array([[1, 23], [2, 34], [3, 45]], + series_type = 'line') - # from a Python dict - my_chart = highcharts.Chart.from_dict(my_dict_obj) + # from a Numpy ndarray, using keyword arguments + my_chart = Chart(data = numpy_array, series_type = 'line') - # from Asana - my_chart = highcharts.Chart.from_asana(project_gid = MY_ASANA_PROJECT_ID) + # from a Numpy ndarray, using the .from_array() method + my_chart = Chart.from_array(data = numpy_array, series_type = 'line') - # from Monday.com - my_chart = highcharts.Chart.from_monday(board_id = MY_MONDAY_BOARD_ID) + # from a JavaScript file + my_chart = Chart.from_js_literal('my_js_literal.js') - # from JIRA - my_chart = highcharts.Chart.from_jira(project_id = MY_JIRA_PROJECT_KEY) + # from a JSON file + my_chart = Chart.from_json('my_json.json') + + # from a Python dict + my_chart = Chart.from_dict(my_dict_obj) # from a Pandas dataframe - my_chart = highcharts.Chart.from_pandas(df, - property_map = { - 'x': 'transactionDate', - 'y': 'invoiceAmt', - 'id': 'id' - }, - series_type = 'line') + my_chart = Chart.from_pandas(df) # from a PySpark dataframe - my_chart = highcharts.Chart.from_pyspark(df, - property_map = { - 'x': 'transactionDate', - 'y': 'invoiceAmt', - 'id': 'id' - }, - series_type = 'line') + my_chart = Chart.from_pyspark(df, + property_map = { + 'x': 'transactionDate', + 'y': 'invoiceAmt', + 'id': 'id' + }, + series_type = 'line') # from a CSV - my_chart = highcharts.Chart.from_csv('/some_file_location/filename.csv' - column_property_map = { - 'x': 0, - 'y': 4, - 'id': 14 - }, - series_type = 'line') + my_chart = Chart.from_csv('/some_file_location/filename.csv') # from a HighchartsOptions configuration object - my_chart = highcharts.Chart.from_options(my_options) + my_chart = Chart.from_options(my_options) - # from a Series configuration - my_chart = highcharts.Chart.from_series(my_series) + # from a Series configuration, using keyword arguments + my_chart = Chart(series = my_series) + # from a Series configuration, using .from_series() + my_chart = Chart.from_series(my_series) 3. Configure Global Settings (optional) ============================================= .. code-block:: python - # Import SharedGanttOptions - from highcharts_gantt.global_options.shared_options import SharedGanttOptions + # Import SharedOptions + from highcharts_gantt.global_options.shared_options import SharedOptions # from a JavaScript file - my_global_settings = SharedGanttOptions.from_js_literal('my_js_literal.js') + my_global_settings = SharedOptions.from_js_literal('my_js_literal.js') # from a JSON file - my_global_settings = SharedGanttOptions.from_json('my_json.json') + my_global_settings = SharedOptions.from_json('my_json.json') # from a Python dict - my_global_settings = SharedGanttOptions.from_dict(my_dict_obj) + my_global_settings = SharedOptions.from_dict(my_dict_obj) # from a HighchartsOptions configuration object - my_global_settings = SharedGanttOptions.from_options(my_options) + my_global_settings = SharedOptions.from_options(my_options) 4. Configure Your Chart / Global Settings @@ -328,6 +323,7 @@ Hello World, and Basic Usage from highcharts_gantt.options.title import Title from highcharts_gantt.options.credits import Credits + # EXAMPLE 1. # Using dicts my_chart.title = { 'align': 'center' @@ -338,7 +334,7 @@ Hello World, and Basic Usage my_chart.credits = { 'enabled': True, - 'href': 'https://www.highcharts.com/', + 'href': 'https://www.highchartspython.com/', 'position': { 'align': 'center', 'vertical_align': 'bottom', @@ -353,17 +349,21 @@ Hello World, and Basic Usage 'text': 'Chris Modzelewski' } + # EXAMPLE 2. # Using direct objects from highcharts_gantt.options.title import Title from highcharts_gantt.options.credits import Credits - my_title = Title(text = 'The Title for My Chart', floating = True, align = 'center') + my_title = Title(text = 'The Title for My Chart', + floating = True, + align = 'center') my_chart.options.title = my_title - my_credits = Credits(text = 'Chris Modzelewski', enabled = True, href = 'https://www.highcharts.com') + my_credits = Credits(text = 'Chris Modzelewski', + enabled = True, + href = 'https://www.highchartspython.com') my_chart.options.credits = my_credits - 5. Generate the JavaScript Code for Your Chart ================================================= @@ -403,6 +403,13 @@ that will render the chart wherever it is you want it to go: my_image_bytes = my_chart.download_chart(filename = 'my_target_file.png', format = 'png') +8. Render Your Chart in a Jupyter Notebook +=============================================== + + .. code-block:: python + + my_chart.display() + -------------- *********************** diff --git a/docs/quickstart.rst b/docs/quickstart.rst index 7ed747c..7565911 100644 --- a/docs/quickstart.rst +++ b/docs/quickstart.rst @@ -114,7 +114,15 @@ Populating Series with Data .. tab:: w/ ``.from_array()`` - .. include:: using/populating_series_data/_with_from_array.rst + .. tabs:: + + .. tab:: Create a New Series + + .. include:: using/populating_series_data/_new_from_array.rst + + .. tab:: Update an Existing Series + + .. include:: using/populating_series_data/_load_from_array.rst .. tab:: from CSV @@ -165,6 +173,10 @@ Assembling Your Chart and Options .. tabs:: + .. tabs:: Using Keyword Arguments + + .. include:: using/assembling_your_chart/_using_kwargs.rst + .. tab:: Using ``.add_series()`` .. include:: using/assembling_your_chart/_using_add_series.rst diff --git a/docs/tutorials.rst b/docs/tutorials.rst new file mode 100644 index 0000000..00ba76e --- /dev/null +++ b/docs/tutorials.rst @@ -0,0 +1,38 @@ +######################################## +Highcharts Core for Python: Tutorials +######################################## + +.. toctree:: + :maxdepth: 2 + + Getting Started + Using Highcharts Core for Python with Pandas + Using Highcharts Core for Python with CSVs + Working with Data in Highcharts for Python + Working with Highcharts for Python Series Instances + Creating JavaScript Callback Functions + Exporting Static Charts + +-------------------- + +.. contents:: + :depth: 2 + :backlinks: entry + +------------------- + +.. tip:: + + You can find even more tutorials on the + `Highcharts website `__. + +Please use the table of contents to the left to find a tutorial that is +relevant to your needs. And if you would like us to prepare additional tutorials, +please: + + #. Check if it has + `already been requested `__ + #. If it has, then please upvote it with a comment. + #. If the tutorial you would like to see hasn't been requested yet, then please + `file a new request `__ - + \ No newline at end of file diff --git a/docs/tutorials/_assembling_a_chart.rst b/docs/tutorials/_assembling_a_chart.rst new file mode 100644 index 0000000..0725a3f --- /dev/null +++ b/docs/tutorials/_assembling_a_chart.rst @@ -0,0 +1,35 @@ + .. code-block:: python + + my_chart = Chart(data = my_iterable, series_type = 'line') + +This will create a :class:`Chart ` instance +with one :term:`series` of type ``line`` (represented as a +:class:`LineSeries ` instance). + +Depending on how we've wrangled our data, we can similarly produce a chart +from a :class:`pandas.DataFrame `, +:class:`numpy.ndarray `, or Python :class:`dict `: + + .. code-block:: python + + # From a Pandas DataFrame + + my_chart = Chart.from_pandas(df, series_type = 'line') + + # From a Numpy ndarray + + my_chart = Chart.from_array(data = as_ndarray, series_type = 'line') + + # From a Python dict + + my_chart = Chart(data = as_dict, series_type = 'line') + +All of these lines of code are equivalent, and should produce an identical +``my_chart``. + + .. seealso:: + + * :doc:`Using Highcharts Core for Python with Pandas ` + * :doc:`Using Highcharts Core for Python with CSVs ` + * :doc:`Working with Highcharts for Python Series Instances ` + * :doc:`Working with Data in the Highcharts for Python Toolkit ` diff --git a/docs/tutorials/callbacks.rst b/docs/tutorials/callbacks.rst new file mode 100644 index 0000000..911d695 --- /dev/null +++ b/docs/tutorials/callbacks.rst @@ -0,0 +1,245 @@ +############################################ +Creating JavaScript Callback Functions +############################################ + +.. contents:: + :depth: 2 + :backlinks: entry + +------------------- + +Highcharts (JS) is known for how easy it is to configure and style +beautiful, interactive data visualizations. One of the core tools +it uses to achieve this is +:term:`callback functions `. + +These can be used throughout your chart configuration, and let you +write custom code that will do what you need it to do in specific +situations. It is incredibly powerful! + +However, Highcharts (JS) is a *JavaScript* suite, and that means +that it only works with *JavaScript* callback functions. So if +we're using **Highcharts for Python**, how do we create JavaScript +:term:`callback functions ` that Highcharts (JS) +will know how to leverage? + +The answer is simple: **Highcharts for Python** provides a special +:class:`CallbackFunction ` +class that you can use to create JavaScript +:term:`callback functions `. When using this class, +you can either: + +#. Write your own JavaScript function and the + :class:`CallbackFunction ` + class will serialize it to JavaScript when needed, or +#. You can write a :term:`callback function` in *Python*, and rely on either + `OpenAI `__'s GPT or `Anthropic `__'s + Claude generative AI model to suggest an equivalent JavaScript function. + +Because you're using **Highcharts for Python**, let's look at the AI-driven +approach first, because we already know how to write Python code. No JavaScript +needed! + +---------------------------- + +******************************************************** +Creating Callback Functions using Generative AI +******************************************************** + +The +:class:`CallbackFunction ` +class has a special helper class method called +:meth:`.from_python() ` +which can automatically create a +:class:`CallbackFunction ` +instance containing the JavaScript function that you need. + +Here's how that works. Let's imagine a scenario where we want a custom tooltip formatter function +that customizes the content of each data point's tooltips. We can write that function in Python like +so: + + .. code-block:: python + + def my_custom_formatter(): + return f'The value for {this.x} is {this.y}.' + +Really pretty straightforward, right? Now we can produce its equivalent by passing +``my_custom_formatter`` as an argument to +:meth:`.from_python() `: + + .. code-block:: python + + my_callback = CallbackFunction.from_python(my_custom_formatter) + +What the +:meth:`.from_python() ` +method call will do is: + + #. It will take the *Python* function's source code, and pass it to the generative AI model of your + choice. + #. The AI will return a *JavaScript* function that the AI believes will do the same thing as your + *Python* function. + #. And it will then load that *JavaScript* function into a new + :class:`CallbackFunction ` + instance. + +Now, when you use this +:class:`CallbackFunction ` +instance in your chart configuration, it will get serialized to its approrpriate *JavaScript* source +code form when appropriate, for example when calling +:meth:`Chart.display() ` or +:meth:`Chart.to_js_literal() `. + +Using Different Models +=============================== + +**Highcharts for Python** supports different models provided by `OpenAI `__ +and `Anthropic `__. + +OpenAI's models in particular differ based on the *version* of GPT that the model supports, as +well as the number of tokens that they allow (more tokens mean they can convert more complicated/longer +function). Most typical callback functions should be converted reasonably reliably using +the default model ``gpt-3.5-turbo``, though others are available: + +* **OpenAI** + + * ``'gpt-3.5-turbo'`` (default) + * ``'gpt-3.5-turbo-16k'`` + * ``'gpt-4'`` + * ``'gpt-4-32k'`` + +* **Anthropic** + + * ``'claude-instant-1'`` + * ``'claude-2'`` + +To use a different model, simply pass the ``model`` argument to the +:meth:`.from_python() ` +method: + + .. code-block:: python + + my_callback = CallbackFunction.from_python(my_custom_formatter, model = "gpt-4") + +Authenticating with Your AI Provider +========================================== + + .. caution:: + + Because this relies on the outside APIs exposed by + `OpenAI `__ and `Anthropic `__, + if you wish to use one of their models you *must* supply your own API key. + These are paid services which they provide, and so you *will* be incurring + costs by using these generative AIs. + +To use one of the supported AI models, you *must* have a valid user/customer account with either +`OpenAI `__ or `Anthropic `__. You must also have +an API key to their respective platform that has permission to use the model you request. You can +set your account up and get the relevant API key from each of the AI providers, respectively. + +When you have the API key, you can pass it in as an argument (``api_key``) to the +:meth:`.from_python() ` +method: + + .. code-block:: python + + my_callback = CallbackFunction.from_python(my_custom_formatter, api_key = "YOUR-API-KEY-GOES-HERE") + +However, if you do not supply an explicit ``api_key`` value, **Highcharts for Python** will look for +the API key in your ``OPENAI_API_KEY`` or ``ANTHROPIC_API_KEY`` environment variables. + +.. tip:: + + **BEST PRACTICE:** Treat your API key as a highly-sensitive piece of information. It should never + be listed in your source code, or in your Jupyter Notebook. It should *only* be read from environment + variables, which in turn should get set with as few places where your API key is visible/available as + possible. + +Reviewing Your JavaScript Code +=================================== + + .. warning:: + + Generating the JavaScript source code is *not* deterministic. + That means that it may not be correct, and we **STRONGLY** + recommend reviewing it before using it in a production + application. + + Every single generative AI is known to have issues - whether + "hallucinations", biases, or incoherence. We cannot stress + enough: + + **DO NOT RELY ON AI-GENERATED CODE IN PRODUCTION WITHOUT HUMAN REVIEW.** + + That being said, for "quick and dirty" EDA, fast prototyping, etc. + the functionality may be "good enough". + +Once you have created a +:class:`CallbackFunction ` +instance using the +:meth:`.from_python() ` +method, you can review the JavaScript source code that was generated by calling ``str()`` on your +:class:`CallbackFunction ` instance: + + .. code-block:: + + print(str(my_callback)) + + # Output: + # function my_custom_formatter() { return 'The value for ' + this.x + ' is ' + this.y + '.'; } + +We **STRONGLY** recommend reviewing the JavaScript source code that was generated before using it in +production. Even if you are not a JavaScript expert, since you know Python and you know what your function *should* +be doing, you can probably follow along close-enough to make sure the JavaScript code "looks right". + + .. tip:: + + **BEST PRACTICE:** Never let the AI generate JavaScript code based on *user-entered* Python code. + + Doing so may introduce unintended security vulnerabilities into your application, and should be + considered *VERY* bad practice. + +------------------------------------- + +******************************************************** +Creating Callback Functions Directly +******************************************************** + +If you do not wish to use generative AI to create your :term:`callback functions `, +you can simply create +:class:`CallbackFunction ` +instances directly. You can do this by: + +Instantiating the ``CallbackFunction`` Directly +=================================================== + +.. code-block:: python + + my_callback = CallbackFunction(function_name = 'my_formatter', + arguments = None, + body = """return 'The value for ' + this.x + ' is ' + this.y + '.';""") + +When instantiating the callback function directly, you supply the body of the function as a string to +the ``body`` argument. A best practice is to use Python's triple-quote syntax to make it easier to +handle quotation marks *within* your JavaScript code. + +Using ``.from_js_literal()`` +================================= + +If you have your JavaScript function in a string, you can use the +:meth:`CallbackFunction.from_js_literal() ` class method to create the callback function instance: + + .. code-block:: python + + callback_as_str = """function my_formatter() { + return 'The value for ' + this.x + ' is ' + this.y + '.'; + }""" + + my_callback = CallbackFunction.from_js_literal(callback_as_str) + +---------- + +And that's it! When your +:class:`CallbackFunction ` +instances are used in your chart configuration, they will automatically be serialized to the +appropriate JavaScript syntax when needed. \ No newline at end of file diff --git a/docs/tutorials/csv.rst b/docs/tutorials/csv.rst new file mode 100644 index 0000000..eb29613 --- /dev/null +++ b/docs/tutorials/csv.rst @@ -0,0 +1,471 @@ +######################################################## +Using Highcharts Core for Python with CSVs +######################################################## + +.. contents:: + :depth: 2 + :backlinks: entry + +------------------- + +CSV (Comma-Separated Value) is one of the most common formats for exchanging +data files - both large and small. Because of its popularity, the +**Highcharts for Python Toolkit** is designed to work seamlessly with it. + +.. sidebar:: What Can Highcharts for Python do with CSVs? + + The **Highcharts for Python Toolkit** is a *visualization* library. It is + not used to perform *arithmetical*, *algebraic*, *statistical*, or *matrix* + calculations. It is exclusively concerned with visualizing data - and that + data is typically the product of calculations that are performed up-stream. + + So given that focus, **Highcharts for Python** is designed to *read* data + that is contained in CSV files and load the data they contain into + appropriate **Highcharts for Python** classes, which can then be rendered + using Highcharts (JS). + +********************************* +General Approach +********************************* + +The **Highcharts for Python Toolkit** provides a number of standard methods +that are used to interact with CSV files. These methods generally take the form: + + * ``.from_csv(as_string_or_file)`` This is always a class method which produces one or more + instances, with data pulled from the CSV content found in ``as_string_or_file``. + * ``.from_csv_in_rows(as_string_or_file)`` This is always a class method which produces one + instance for every *row* in the CSV (``as_string_or_file``). + * ``.load_from_csv(as_string_or_file)`` This is an instance method which updates an instance + with data read from the ``as_string_or_file`` argument. + +.. tip:: + + All three of these standard methods are packaged to have batteries included. + This means that for simple use cases, you can simply pass a + CSV to the method, and the method wlil attempt to determine the best way to + deserialize the CSV into the appropriate **Highcharts for Python** objects. + + However, if you find that you need more fine-grained control, the methods + provide powerful tools to give you the control you need when you need it. + +These standard methods - with near-identical syntax - are available: + + * On all :term:`series` classes (descended from + :class:`SeriesBase `) + * On the :class:`Chart ` class + +------------------------------- + +***************************** +Preparing Your CSV Data +***************************** + +So let's try a real-world example. Let's say you've got some annual population +counts stored in a CSV file named ``'census-time-series.csv'`` that looks like this: + + .. image:: /_static/tutorials/census-time-series-csv-01.png + :width: 100% + :alt: Rendering of the data contained in 'census-time-series.csv' + +The first column contains the names of geographic regions, while each of the subsequent +columns contains the population counts for a given year. Now, let's say we wanted +to visualize this data in various ways. + +------------------------------ + +********************************************* +Creating the Chart: ``Chart.from_csv()`` +********************************************* + +Relying on the Defaults +=========================== + +The simplest way to create a chart from a CSV file is to call +:class:`Chart.from_csv() ` like +so: + +.. code-block:: python + + my_chart = Chart.from_csv('census-time-series.csv', + wrapper_character = '"') + my_chart.display() + +.. image:: /_static/tutorials/census-time-series-03.png + :width: 100% + :alt: Rendering of the chart produced by Chart.from_csv('census-time-series.csv') + +As you can see, we haven't provided any more instructions besides telling it to +generate a chart from the file ``'census-time-series.csv'``, and to interpret a single +quotation mark as a wrapper character. The result is a line chart, with one series for +each year, and one point for each region. + + .. tip:: + + Unless instructed otherwise, **Highcharts for Python** will default to using a + line chart. + +Setting the Series Type +============================== + +Why don't we switch it to a bar chart? + +.. code-block:: python + + my_chart = Chart.from_csv('census-time-series.csv', + series_type = 'bar', + wrapper_character = '"') + +.. image:: /_static/tutorials/census-time-series-04.png + :width: 100% + :alt: Rendering of the chart produced by Chart.from_csv('census-time-series.csv', series_type = 'bar') + +Now the result is a little more readable, but still not great: After all, there are more than +fifty geographic regions represented for each year, which makes the chart super crowded. +Besides, maybe we're only interested in a specific year: 2019. + +Let's try focusing our chart. + +Basic Property Mapping +========================== + +.. code-block:: python + + my_chart = Chart.from_csv('census-time-series.csv', + series_type = 'bar', + property_column_map = { + 'x': 'Geographic Area', + 'y': '2019' + }) + +.. image:: /_static/tutorials/census-time-series-05.png + :width: 100% + :alt: Rendering of the chart produced by Chart.from_csv('census-time-series.csv', series_type = 'bar', property_column_map = {'x': 'Geographic Area', 'y': '2019'}) + +Much better! We've now added a ``property_column_map`` argument to the ``.from_csv()`` method call. +This argument tells Highcharts for Python how to map columns in your data to properties in the +resulting chart. In this case, the keys ``'x'`` and ``'y'`` tell **Highcharts for Python** that you want to map the ``'Geographic Area'`` column to the resulting series' data points' +:meth:`.x `, +and to map the ``'2019'`` column to the :meth:`.y ` +properties, respectively. + +The net result is that ``my_chart`` contains one +:class:`BarSeries ` whose +:meth:`.data ` property contains a +:class:`BarDataCollection ` instance populated +with the data from the ``'Geographic Area'`` and ``'2019'`` columns in ``census-time-series.csv``. + +But maybe we actually want to compare a couple different years? Let's try that. + + .. tip:: + + Not all CSV data contains a header row. If your CSV data does not contain a header row, + ``property_column_map`` accepts :class:`int ` values, which indicate the + index of the column that you want to map. So the method call above would be equivalent to: + + .. code-block:: python + + my_chart = Chart.from_csv('census-time-series.csv', + series_type = 'bar', + property_column_map = { + 'x': 0, + 'y': 10 + }) + +Property Mapping with Multiple Series +======================================== + +.. code-block:: python + + my_chart = Chart.from_csv('census-time-series.csv', + series_type = 'column', + property_column_map = { + 'x': 'Geographic Area', + 'y': ['2017', '2018', '2019'] + }) + +.. image:: /_static/tutorials/census-time-series-06.png + :width: 100% + :alt: Rendering of the chart produced by Chart.from_csv('census-time-series.csv', series_type = 'bar', property_column_map = {'x': 'Geographic Area', 'y': ['2017', '2018', '2019']}) + +Now we're getting somewhere! We've added a list of column names to the ``'y'`` key in the ``property_column_map`` +argument. Each of those columns has now produced a *separate* +:class:`BarSeries ` instance - but they're +all still sharing the ``'Geographic Area'`` column as their +:meth:`.x ` value. + + .. note:: + + You can supply multiple values to any property in the ``property_column_map``. The example + provided above is equivalent to: + + .. code-block:: python + + my_chart = Chart.from_csv('census-time-series.csv', + series_type = 'column', + property_column_map = { + 'x': ['Geographic Area', 'Geographic Area', 'Geographic Area'], + 'y': ['2017', '2018', '2019'] + }) + + The only catch is that the ultimate number of values for each key must match. If there's + only one value, then it will get repeated for all of the others. But if there's a mismatch, + then **Highcharts for Python** will throw a + :exc:`HighchartsCSVDeserializationError `. + +But so far, we've only been using the ``'x'`` and ``'y'`` keys in our ``property_column_map``. What if we wanted to +configure additional properties? Easy! + +Configuring Additional Properties +===================================== + +.. code-block:: python + + my_chart = Chart.from_csv('census-time-series.csv', + series_type = 'bar', + property_column_map = { + 'x': 'Geographic Area', + 'y': ['2017', '2018', '2019'], + 'id': 'some other column' + }) + +Now, our CSV is pretty simple does not contain a column named ``'some other column'`. But *if* it did, +then it would use that column to set the :meth:`.id ` property of each data point. + + .. note:: + + You can supply any property you want to the ``property_map``. If the property is not + supported by the series type you've selected, then it will be ignored. + +But our chart is still looking a little basic - why don't we tweak some series configuration options? + +Configuring Series Options +=============================== + +.. code-block:: python + + my_chart = Chart.from_csv('census-time-series.csv', + series_type = 'bar', + property_column_map = { + 'x': 'Geographic Area', + 'y': ['2017', '2018', '2019'], + }, + series_kwargs = { + 'point_padding': 5 + }) + +.. image:: /_static/tutorials/census-time-series-07.png + :width: 100% + :alt: Rendering of the chart produced by Chart.from_csv('census-time-series.csv', series_type = 'bar', property_column_map = {'x': 'Geographic Area', 'y': ['2017', '2018', '2019'], 'id': 'Geographic Area'}, series_kwargs = {'point_padding': 0.25}) + +As you can see, we supplied a new ``series_kwargs`` argument to the ``.from_csv()`` method call. This +argument receives a :class:`dict ` with keys that correspond to properties on the series. In +this case, by supplying ``'point_padding'`` we have set the resulting +:meth:`BarSeries.point_padding ` property to a +value of ``0.25`` - leading to a bit more spacing between the bars. + +But our chart is *still* a little basic - why don't we give it a reasonable title? + +Configuring Options +============================= + +.. code-block:: python + + my_chart = Chart.from_csv('census-time-series.csv', + series_type = 'bar', + wrapper_character = '"', + property_column_map = { + 'x': 'Geographic Area', + 'y': ['2017', '2018', '2019'] + }, + series_kwargs = { + 'point_padding': 5 + }, + options_kwargs = { + 'title': { + 'text': 'This Is My Chart Title' + } + }) + +.. image:: /_static/tutorials/census-time-series-08.png + :width: 100% + :alt: Rendering of the chart produced by Chart.from_csv('census-time-series.csv', series_type = 'bar', property_column_map = {'x': 'Geographic Area', 'y': ['2017', '2018', '2019'], 'id': 'Geographic Area'}, series_kwargs = {'point_padding': 0.25}, options_kwargs = {'title': {'text': 'This Is My Chart Title'}}) + +As you can see, we've now given our chart a title. We did this by adding a new ``options_kwargs`` argument, +which likewise takes a :class:`dict ` with keys that correspond to properties on the chart's +:class:`HighchartsOptions ` configuration.` + +Now let's say we wanted our chart to render in an HTML ``
`` with an ``id`` of ``'my_target_div`` - +we can configure that in the same method call. + +Configuring Chart Settings +============================== + +.. code-block:: python + + my_chart = Chart.from_csv('census-time-series.csv', + series_type = 'bar', + wrapper_character = '"', + property_column_map = { + 'x': 'Geographic Area', + 'y': ['2017', '2018', '2019'], + }, + series_kwargs = { + 'point_padding': 0.25 + }, + options_kwargs = { + 'title': { + 'text': 'This Is My Chart Title' + } + }, + chart_kwargs = { + 'container': 'my_target_div' + }) + +While you can't really *see* the difference here, by adding the ``chart_kwargs`` argument to +the method call, we now set the :meth:`.container ` property +on ``my_chart``. + +But maybe we want to do something a little different - like compare the change in population over time. +Well, we can do that easily by visualizing each *row* of ``census-time-series.csv`` rather than each *column*.` + +Visualizing Data in Rows +============================== + +.. code-block:: python + + my_chart = Chart.from_csv('census-time-series.csv', + series_type = 'line', + series_in_rows = True, + wrapper_character = '"') + +.. image:: /_static/tutorials/census-time-series-09.png + :width: 100% + :alt: Rendering of the chart produced by Chart.from_csv('census-time-series.csv', series_type = 'line', series_in_rows = True) + +Okay, so here we removed some of the other arguments we'd been using to simplify the example. You'll see we've now +added the ``series_in_rows`` argument, and set it to ``True``. This tells **Highcharts for Python** that we expect +to produce one series for every *row* in ``census-time-series.csv``. + +Because we have not specified a ``property_column_map``, the series +:meth:`.name ` values are populated from the ``'Geographic Area'`` +column, while the data point :meth:`.x ` values come from each additional column (e.g. ``'2010'``, ``'2011'``, ``'2012'``, etc.) + + .. tip:: + + To simplify the code further, any class that supports the ``.from_csv()`` method also + supports the ``.from_csv_in_rows()`` method. The latter method is equivalent to passing + ``series_in_rows = True`` to ``.from_csv()``. + + For more information, please see: + + * :class:`Chart.from_csv_in_rows() ` + * :class:`SeriesBase.from_csv_in_rows() ` + +But maybe we don't want *all* geographic areas shown on the chart - maybe we only want to compare a few. + +Filtering Rows +======================= + +.. code-block:: python + + my_chart = Chart.from_csv('census-time-series.csv', + series_type = 'line', + series_in_rows = True, + wrapper_character = '"', + series_index = slice(7, 10)) + +.. image:: /_static/tutorials/census-time-series-10.png + :width: 100% + :alt: Rendering of the chart produced by Chart.from_csv('census-time-series.csv', series_type = 'line', series_in_rows = True, series_index = slice(7, 10)) + +What we did here is we added a ``series_index`` argument, which tells **Highcharts for Python** to only +include the series found at that index in the resulting chart. In this case, we supplied a :func:`slice ` +object, which operates just like ``list_of_series[7:10]``. The result only returns those series between index 7 and 10. + +------------------------ + +********************************************************************** +Creating Series: ``.from_csv()`` and ``.from_csv_in_rows()`` +********************************************************************** + +All **Highcharts for Python** :term:`series` descend from the +:class:`SeriesBase ` class. And they all +therefore support the ``.from_csv()`` class method. + +When called on a series class, it produces one or more series from the CSV supplied. +The method supports all of the same options +as :class:`Chart.from_csv() ` *except for* ``options_kwargs`` and +``chart_kwargs``. This is because the ``.from_csv()`` method on a series class is only responsible for +creating series instances - not the chart itself. + +Creating Series from Columns +================================== + +So let's say we wanted to create one series for each of the years in ``census-time-series.csv``. +We could do that like so: + + .. code-block:: python + + my_series = BarSeries.from_csv('census-time-series.csv') + +Unlike when calling :meth:`Chart.from_csv() `, we +did not have to specify a ``series_type`` - that's because the ``.from_csv()`` class method on a +series class already knows the series type. + +In this case, ``my_series`` now contains ten separate :class:`BarSeries ` +instances, each corresponding to one of the year columns in ``census-time-series.csv``. + +But maybe we wanted to create our series from rows instead? + +Creating Series from Rows +================================= + +.. code-block:: python + + my_series = LineSeries.from_csv_in_rows('census-time-series.csv') + +This will produce one :class:`LineSeries ` +instance for each row in ``census-time-series.csv``, ultimately producing a +:class:`list ` of 57 +:class:`LineSeries ` instances. + +Now what if we don't need all 57, but instead only want the first five? + +Filtering Series Created from Rows +====================================== + +.. code-block:: python + + my_series = LineSeries.from_csv_in_rows('census-time-series.csv', series_index = slice(0, 5)) + +This will return the first five series in the list of 57. +-------------------------- + +*********************************************************** +Updating an Existing Series: ``.load_from_csv()`` +*********************************************************** + +So far, we've only been creating new series and charts. But what if we want to update +the data within an existing series? That's easy to do using the +:meth:`.load_from_csv() ` method. + +Let's say we take the first series returned in ``my_series`` up above, and we want to replace +its data with the data from the *10th* series. We can do that by: + + .. code-block:: python + + my_series[0].load_from_csv('census-time-series.csv', + series_in_rows = True, + series_index = 9) + +The ``series_in_rows`` argument tells the method to generate series per row, and then +the ``series_index`` argument tells it to only use the 10th series generated. + + .. caution:: + + While the :meth:`.load_from_csv() ` + method supports the same arguments as + :meth:`.from_csv() `, it expects that + the arguments supplied lead to an unambiguous *single* series. If they are ambiguous - meaning they + lead to multiple series generated from the CSV - then the method will throw a + :exc:`HighchartsCSVDeserializationError ` \ No newline at end of file diff --git a/docs/tutorials/data.rst b/docs/tutorials/data.rst new file mode 100644 index 0000000..5ff5dc2 --- /dev/null +++ b/docs/tutorials/data.rst @@ -0,0 +1,412 @@ +######################################################## +Using Highcharts Core for Python with Pandas +######################################################## + +.. contents:: + :depth: 2 + :backlinks: entry + +------------------- + +The **Highcharts for Python Toolkit** is a data *visualizaiton* library. +That means that it is designed to let you visualize the data that you +or your users are analyzing, rather than to do the analysis itself. But +while there are better tools to actually crunch the numbers, +**Highcharts for Python** still has to work closely with your data in +order to visualize it. + +When working with **Highcharts for Python**, it can be useful to +understand: + + #. How **Highcharts for Python** represents your data + #. How to load your data into a **Highcharts for Python** object + #. How to adjust your data in **Highcharts for Python** + #. How **Highcharts for Python** serializes your data for + Highcharts (JS). + +------------------- + +************************************* +Highcharts for Python Data Model +************************************* + +In broad brushstrokes, you can think of your **Highcharts for Python** +chart as a tree. + +.. list-table:: + :widths: 30 70 + + * - .. image:: /_static/highcharts-chart-anatomy.png + :width: 100% + :alt: Diagram of chart structure: Chart > Options > Series Collection > Series > Data Collection > Data Point + +At the root of the tree is a +:class:`Chart `, and that chart contains +options (:class:`HighchartsOptions `). +Those options in turn contain a collection of :term:`series`, +each of which can be thought of as one "line" of data in your visualization. + +Each series instance (descended from +:class:`SeriesBase `) +contains a :meth:`.data ` +property, which contains a set of :term:`data points `. + +Depending on your data and your configuration, this set of data points may +be represented as: + + * a :class:`DataPointCollection ` + instance (or a descendent of it) which in turn contains your data values and related + configuration options + * an iterable of + :class:`DataBase `-descended instances, + each of which contains the data value and configuration of an invidual :term:`data point` + +This model is relatively straightforward, but there is one important complexity: the +relationship between +:class:`DataPointCollection ` +instances and :class:`DataBase ` instances. + +:class:`DataPointCollection ` vs :class:`DataBase ` +======================================================================================================================================================================================= + +The :class:`DataPointCollection ` +class stores your individual data points in a combination of three different list-like structures: + + * as a :class:`numpy.ndarray ` in the + :meth:`.ndarray ` property + * as a :class:`list ` of + :class:`DataBase ` instances in the + :meth:`.data_points ` + property + * as a :class:`list ` of primitives (e.g. numbers, strings, etc.) in the + :meth:`.array ` + property + +Why split it up like this? The purpose is to maximize performance within both +**Highcharts for Python** and Highcharts (JS), while still minimizing outside dependencies. + +Highcharts (JS) supports data organized in primitive arrays. So it can easily visualize something +like the following: + + .. code-block:: python + + [ + [0, 12], + [1, 34], + [2, 56], + [3, 78], + [4, 90] + ] + +This way of representing your data gives you the fastest performance in Highcharts (JS), +leading to lightening-fast rendering of your chart. And since it's just a simple list of +numbers, **Highcharts for Python** doesn't have to apply any fancy logic to serialize it to +:term:`JS literal notation ` - leading to fast +performance in Python as well. + +This is why the +:class:`DataPointCollection ` +separates the data that can be represented as a primitive array (stored in either +:meth:`.ndarray ` or +:meth:`.array `), from +data point properties that need to be represented as a full Highcharts (JS) data point object +(stored in +:meth:`.data_points `). + +And if you're familiar with `NumPy `__, that looks *just* like +a :class:`ndarray ` - and for good reason! If you have +`NumPy ` installed, **Highcharts for Python** will leave your +:class:`ndarray ` objects as-is to benefit from its vectorization +and performance. + +Internally, +:class:`DataPointCollection ` +instances will intelligently combine the information stored in these three different properties +to serialize your data points. This is done as-appropriately, generating a list of renderable +data points represented either as a primitive array, or as full objects, depending on the +properties that have been configured. + +So do you have to worry to about this complexity? Not really! All of this happens under the +hood in the **Highcharts for Python** code. You can simply load your data using the +convenience methods available on your series instances +:class:`DataPointCollection ` +or its descendents, or simply pass your data to the series +:class:`.data ` property. + +Let's see how this works in practice. + +------------------------ + +***************************************** +Loading Data into Highcharts for Python +***************************************** + +Preparing Your Data +=========================== + +So let's try a real-world example. Let's say you've got some annual population +counts stored in a CSV file named ``'census-time-series.csv'``. There are four +different ways you can represent this data: + + #. As-is in the CSV file. Meaning you don't do anything, just leave it + in the file as-is. + #. Loaded into a Python iterable (i.e. a :class:`list ` of + :class:`list `, where each inner list represents a row from + the CSV). This might look something like this: + + .. code-block:: python + + raw_data = [ + ['United States', 309321666, 311556874, 313830990, 315993715, 318301008, 320635163, 322941311, 324985539, 326687501, 328239523], + ['Northeast', 55380134, 55604223, 55775216, 55901806, 56006011, 56034684, 56042330, 56059240, 56046620, 55982803], + ['Midwest', 66974416, 67157800, 67336743, 67560379, 67745167, 67860583, 67987540, 68126781, 68236628, 68329004], + ... + ] + + #. As a :class:`numpy.ndarray `, which might look like this: + + .. list-table:: + :widths: 30 70 + + - .. image:: /_static/tutorials/raw-data-as-numpy.png + :width: 100% + :alt: Rendering of the numpy.ndarray produced by np.genfromtext('census-time-series.csv', delimiter = ',', names = True) + + #. As a :class:`pandas.DataFrame `, which might look like this: + + .. list-table:: + :widths: 30 70 + + * - .. code-block:: python + + raw_data = pandas.read_csv('census-time-series.csv', + index_col = 0, + thousands = ',', + delimiter = ',') + + * - .. image:: /_static/tutorials/census-time-series-02.png + :width: 100% + :alt: Rendering of the Pandas DataFrame loaded from "census-time-series.csv" + +Now that we've got our data prepared, let's add it to a series or chart. + +Creating a Series/Chart with Data +====================================== + +.. note:: + + In this tutorial, we'll focus on assembling one or more :term:`series` of data, rather than + a complete chart. This is because chart's have many more configuration options, but + fundamentally the data that they contain is stored within one or more series instances, + which themselves contain data points in a + :class:`DataPointCollection ` + or an iterable of + :class:`DataBase ` instances. + +So now that we have ``raw_data`` prepared, we can now load it into a series. There are four ways to do +this: + + #. By passing it to the :meth:`.data ` property + of our series when instantiating the series: + + .. code-block:: python + + from highcharts_gantt.options.series.area import LineSeries + + my_series = LineSeries(data = raw_data) + + #. By calling one of the "helper" methods: + + .. code-block:: python + + from highcharts_gantt.options.series.area import LineSeries + + # If my data is either a numpy.ndarray or Python iterable + my_series = LineSeries.from_array(raw_data) + + # If my data is in a Pandas DataFrame + my_series = LineSeries.from_pandas(raw_data) + + # If my data is in a CSV file + my_series = LineSeries.from_csv('census-time-series.csv') + + .. seealso:: + + Depending on the arguments you supply to the helper methods, they + may produce *multiple* series for inclusion on your chart. For more + information, please see: + + * :doc:`Using Highcharts for Python with Pandas ` + * :doc:`Using Highcharts for Python with CSVs ` + + #. By instantiating your *set* of data directly, and passing it to the + :meth:`.data ` property + of our series: + + .. code-block:: python + + from highcharts_gantt.options.series.area import LineSeries + from highcharts_gantt.options.series.data.cartesian import CartesianData + + my_data = CartesianData.from_array(raw_data) + + my_series = LineSeries(data = my_data) + + .. seealso:: + + Depending on the arguments you supply to the helper methods, they + may produce *multiple* series for inclusion on your chart. For more + information, please see: + + * :doc:`Using Highcharts for Python with Pandas ` + * :doc:`Using Highcharts for Python with CSVs ` + + #. By instantiating *individual* data points directly, and passing it to + the :meth:`.data ` property + of our series: + + .. code-block:: python + + from highcharts_gantt.options.series.area import LineSeries + from highcharts_gantt.options.series.data.cartesian import CartesianData + + my_data = [CartesianData(x = record[0], y = record[1] for record in raw_data] + + my_series = LineSeries(data = my_data) + + +In all cases, the result is the same: a +:class:`LineSeries ` instance (or a +:class:`list ` of +:class:`LineSeries ` that contain your data. + +Now that your data has been loaded into your series, you can configure it as needed. + +Configuring Your Data +========================================= + +In most cases, you shouldn't have to worry about the internals of how **Highcharts for Python** +stores your data. Depending on whether you supplied a primitive array, a +:class:`numpy.ndarray `, or data from a Pandas +:class:`DataFrame `, your series' data will either be represented as +a :class:`DataPointCollection ` +or as a :class:`list ` of data point objects (descended from +:class:`DataBase `). + +In all cases, you can easily set properties on your data via your series object itself. For +example, let's say we wanted to configure the +:meth:`.target ` values on data points +in a :class:`BulletSeries ` instance. We +can do that easily by working at the *series* level: + + .. code-block:: python + + # EXAMPLE 1. + # Supplying one value per data point. + + my_series.target = [1, 2, 3, 4, 5, 6] + + # EXAMPLE 2. + # Supplying one value, which will be applied to ALL data points. + + my_series.target = 2 + +This propagation of data point properties extends to *all* data point properties. If a +property of the same name exists on the series, it will be set on the *series*. But if +it only exists on the data point, it will be propagated to the relevant data points. + +In some circumstances, you may want to set data point properties that have identically-named +properties on the series. For example, data points and series both support the ``.id`` property. +But you can set this property at the data point level in two ways: + + #. If your data point is represented as a + :class:`DataPointCollection `, + you can simply set it as a sub-property of the series + :meth:`.data ` property: + + .. code-block:: python + + # EXAMPLE 1. + # Supplying one value per data point. + my_series.data.id = ['id1', 'id2', 'id3', 'id4', 'id5', 'id6'] + + # EXAMPLE 2. + # Supplying one value, which will be applied to ALL data points. + + my_series.data.id = 'id2' + + The :class:`DataPointCollection ` + will worry about proagating the relevant property / value to the individual data points as needed. + + #. If you data points are represented as a :class:`list ` of + :class:`DataBase `-descended objects, then you can + adjust them the same way you would adjust any member of a list: + + .. code-block:: + + id_list = ['id1', 'id2', 'id3', 'id4', 'id5', 'id6'] + for index in range(len(series.data)): + series.data[index].id = id_list[index] + + In this case, you are adjusting the data points directly, so you do need to make sure you are + adjusting the exact properties you need to adjust in the exact right location. + +Updating Your Data +======================== + +You can also update your data after it has been loaded into your series. This is done by calling one +of the ``.load_from_*`` series helper methods, which makes it possible to update your series' data +just like when creating the series: + + .. code-block:: python + + # EXAMPLE 1. + # Updating the .data property + + my_series.data = updated_data + + # EXAMPLE 2. + # If my data is either a numpy.ndarray or Python iterable + + my_series.load_from_array(updated_data) + + # EXAMPLE 3. + # If my data is in a Pandas DataFrame + + my_series.load_from_pandas(updated_data) + + # EXAMPLE 4. + # If my data is in a CSV file + + my_series.load_from_csv('updated-data.csv') + +--------------------------- + +*************************************** +Serializing Your Data for Rendering +*************************************** + +While you shouldn't have to serialize your data directly using **Highcharts for Python**, it +may be useful to understand how this process works. + +First, it's important to understand that Highcharts (JS) supports data represented in two different +forms: + + * as :term:`JavaScript literal objects `, and + * as primitive arrays, which are basically collections of strings and numbers + +JS literal objects are the most flexible, because they allow you to take advantage of all of the +different data point configuration options supported by Highcharts. However, primitive arrays +perform much faster: Highcharts for Python generates them faster, there's less data to transfer on the wire, and Highcharts (JS) can render them faster. + +For this reason, **Highcharts for Python** will always try to serialize your data points to a +primitive array first. If the series type supports a primitive array, and there is no information configured +on the data points that prevents it from being serialized as a primitive array, Highcharts for Python +will default to that form of serialization. + +However, if there are special properties (not supported by primitive arrays) set on the data points, or if +the series type is one that does not support primitive arrays, then Highcharts for Python will generate +a JavaScript literal object instead. + +This logic all happens automatically whenever you call +:class:`.to_js_literal() ` on your series. diff --git a/docs/tutorials/exporting.rst b/docs/tutorials/exporting.rst new file mode 100644 index 0000000..33897f8 --- /dev/null +++ b/docs/tutorials/exporting.rst @@ -0,0 +1,161 @@ +####################################################### +Exporting Static Charts with Highcharts for Python +####################################################### + +.. contents:: + :depth: 2 + :backlinks: entry + +------------------- + +Highcharts (JS) and **Highcharts for Python** are designed to produce beautiful, powerful, highly-interactive +data visualizations. However, there are moments when you or your users want to create a static chart for inclusion in +presentations, documents, etc. Highcharts has you covered for that as well! + +------------ + +********************** +Client-side Exporting +********************** + +Using **Highcharts for Python**, you can configure client-side export within the +:meth:`Chart.options ` settings by configuring the +:meth:`options.exporting ` +property. + +In particular, you can apply configuration through an +:class:`Exporting ` instance, which +lets you configure how your chart will support exporting from within the user's +browser when the chart is rendered. Here’s a quick example, assuming you have a +:meth:`Chart `` instance called ``my_chart``: + + .. code-block:: python + + from highcharts_gantt.options.exporting import Exporting + + exporting_options = Exporting(enabled = True, + filename = 'your-exported-chart', + show_table = True, + table_caption = "Your Chart's Caption Goes Here", + accessibility = { + 'enabled': True + }) + + exporting_options.buttons['contextButton'].menu_items = ['printChart', 'separator', 'downloadPNG'] + + my_chart.options.exporting = exporting_options + +And that's it. With the code above you've now configured some basic logic that: + + * Enables client-side export of the ``my_chart`` visualization. + * Gives an exported chart image a default filename of ``'your-exported-chart'`` (not + including the file's extension). + * Makes sure that the exported or printed version of your chart includes the chart's + underlying data table (thanks to the ``show_table`` property being set to ``True``), + and + * Gives users the ability to either print the chart or download a PNG version of the chart, + but nothing else (by setting the relevant buttons shown in the context menu). + +Highcharts Core supports client-side export to a number of formats, including: + + * PNG + * JPEG + * SVG + * CSV + * Excel + +And you can also configure the client-side export to fall back to server-side export should it fail. + + .. seealso:: + + * For more details on the extensive options, please see :mod:`highcharts_gantt.options.exporting` + +--------- + +************************************** +Programmatic (Server-side) Export +************************************** + +So now that your users can download a PNG image of your chart, maybe you want to create an image programmatically. **Highcharts for Python** makes that possible through an integration with the **Highcharts Export Server**. It's actually trivially easy to do since you can do it with just one method call in your Python code: + + .. code-block:: python + + # EXAMPLE 1. + # Download a PNG version of the chart in memory within your Python code. + + my_png_image = my_chart.download_chart(format = 'png') + + # EXAMPLE 2. + # Download a PNG version of the chart and save it the file "/images/my-chart-file.png" + my_png_image = my_chart.download_chart( + format = 'png', + filename = '/images/my-chart-file.png' + ) + +The two examples shown above both download a PNG of your chart: + + #. The first example keeps that PNG image in your Python code only, storing its binary data in the + ``my_png_image`` variable. + #. The second example not only stores its binary data in the ``my_png_image`` variable, but it *also* saves + the PNG image to the file ``'/images/my-chart-file.png'``. + +The format argument is really the one doing the heavy lifting above. In the example above, it tells the method to generate a PNG image, but you can also create: + + * ``'jpeg'`` + * ``'pdf'``, and + * ``'svg' + +And that's it! There's really nothing simpler. + +.. note:: + + Under the hood, this method defaults to calling the Highcharts Export Server that is maintained by + `Highsoft `__ (creators of Highcharts Core (JS)). This publicly-available server + is available to all licensees of Highcharts Core, and you are free to use it to generate downloadable + versions of your data visualizations. + + However, it is rate-limited and it does mean transmitting your chart's data across the wire. There are + various situations in which this is inappropriate, which is why Highsoft allows you to configure and deploy + your own Highcharts Export Server. And Highcharts for Python supports using your own custom Export Server + for your programmatic chart exports. + +------------------------ + +************************************ +Using a Custom Export Server +************************************ + +.. tip:: + + While deploying your own Highcharts Export Server is beyond the scope of this tutorial, we strongly recommend that + you review the + `Highcharts Export Server documentation `__ + +If you have your own Highcharts Export Server, you can override **Highcharts for Python**'s default to +have your code rely on your own export server. While you can do this by creating an instance of +:class:`highcharts_gantt.headless_export.ExportServer` with your custom configuration and passing it to the +:meth:`.download_chart() ` method in the ``server_instance`` +argument, it is far easier to simply set some environment variables wherever your Python code will be running: + + * ``HIGHCHARTS_EXPORT_SERVER_DOMAIN`` is the environment variable that specifies the domain where the Highcharts + Export Server exists. If this environment variable is not set, it will default to ``"export.highcharts.com"``, + which is the Highsoft-provided export server. + * ``HIGHCHARTS_EXPORT_SERVER_PATH`` is the path at the domain where your Export Server is reachable. If this + environment variable is not set, it will default to :obj:`None ` since there is no path when using the + Highsoft-provided export server. + * ``HIGHCHARTS_EXPORT_SERVER_PORT`` is the port where your Export Server is reachable. If this environment variable + is not set, it will default to :obj:`None ` since there is no need to specify a port when using the + Highsoft-provided export server. + +In addition to the three environment variables above, the +:meth:`.download_chart() ` method also supports +three additional arguments which may prove useful: + + * ``auth_user`` which is the user to supply to your custom Export Server using Basic authentication. This defaults to + :obj:`None ` (since the default Highsoft-provided Export Server has no authentication). + * ``auth_password`` which is the password to supply to your custom Export Server using Basic authentication. This + :obj:`None ` (since the default Highsoft-provided Export Server has no authentication). + * ``timeout`` which is the number of seconds to wait before issuing a timeout error. The timeout check is passed if + any bytes have been received on the socket in less than this number of seconds. It defaults to ``0.5``, but you may + want to adjust this when using your own custom Export Server. + diff --git a/docs/tutorials/getting_started.rst b/docs/tutorials/getting_started.rst new file mode 100644 index 0000000..41ffb21 --- /dev/null +++ b/docs/tutorials/getting_started.rst @@ -0,0 +1,274 @@ +############################################## +Highcharts Core for Python: Getting Started +############################################## + +.. contents:: + :depth: 2 + :backlinks: entry + +------------------- + +We're so glad you want to use **Highcharts Core for Python**! This tutorial will help +you get started, and give you a jumping off point to dive into all the great features +of the Highcharts visualization suite. Before you really get going, we suggest you take +a look at all of the great :doc:`visualization types <../visualizations>` you can create +using **Highcharts for Python** and `Highcharts (JS) `__. + +*************************** +Installation +*************************** + +First things first, to use **Highcharts for Python** the first step is to install the +library (likely to a virtual environment). That's pretty straightforward: + +.. include:: ../_installation.rst + +------------------- + +****************************************** +Importing Highcharts Core for Python +****************************************** + +Once you've installed **Highcharts Core for Python**, you can import it into your project +in two different ways: + +.. include:: ../using/_importing.rst + +------------------- + +****************************************** +Wrangle Your Data +****************************************** + +Since you want to use **Highcharts Core for Python** to visualize some data, first +you're going to have to wrangle the data into a form you can work with. How you do +this really depends on the data you are working with and the other tools you are +using in your tech stack. + +The **Highcharts for Python** toolkit works with most of the "usual suspects" in the +Python ecosystem, including: + + * `Pandas `__ + * `Numpy `__ + * `PySpark `__ + * CSV files + * JSON files + * Python :class:`dict ` instances + * Python iterables (e.g. :class:`list `, :class:`tuple `, etc.) + +For the sake of simplicity, we'll work with Python iterables to show how you +can quickly get started. Let's say we have a simple 2-dimensional set of x/y values +that we want to plot: + + .. code-block:: python + + my_iterable = [ + [0, 123], + [1, 456], + [2, 789], + [3, 987], + [4, 654], + [5, 321] + ] + +That's all I need to wrangle my data! **Highcharts for Python** can work with +``my_iterable`` directly and easily, wherever data is referenced. + +.. tip:: + + Different Highcharts :term:`series` types support different structures of + iterable. + + Please review the detailed :ref:`series documentation ` for + series type-specific details of relevant iterable/array structures. + +Alternatively, we can convert ``my_iterable`` into a +:class:`pandas.DataFrame `, :class:`numpy.ndarray `, +or Python :class:`dict `: + + .. code-block:: python + + # As a Pandas DataFrame + df = pandas.DataFrame(my_iterable, columns=['x', 'y']) + + # As a Numpy ndarray + as_ndarray = numpy.as_ndarray(my_iterable) + + # As a Python dict + as_dict = {'x': x[0], 'y': x[1] for x in my_iterable} + +Now, we can consider our data "wrangled" and ready for visualization. + +-------------------- + +****************************************** +Assembling a Chart +****************************************** + +With our data wrangled, we can construct a chart with one line of code: + +.. include:: _assembling_a_chart.rst + +********************************** +Configuring the Chart +********************************** + +`Highcharts (JS) `__ sets the standard for +data visualization because it supports a huge number of easy-to-understand +configuration options. **Highcharts for Python** makes it easy to configure +any of those options directly within Python. + +To do that, we can use the :meth:`Chart.options ` +property. Having assembled our chart following the instructions above, ``my_chart`` +already contains a :class:`HighchartsOptions ` +instance in the :meth:`Chart.options ` property. +You can access the :class:`LineSeries ` +we created at :meth:`Chart.options.series `, +and you can set any other options you need on +:meth:`Chart.options `. + +For example, let's say we want to set the chart's title to "My First Chart". To do that, +we can configure the +:meth:`Chart.options.title ` property +using either a :class:`Title ` instance, or a +:class:`dict `: + + .. code-block:: python + + # as a Title instance + + from highcharts_gantt.options.title import Title + + my_chart.options.title = Title(text = 'My First Chart') + + # as a dict + + my_chart.options.title = {'text': 'My First Chart'} + +Either way, the chart's title will now be set to "My First Chart". + + .. seealso:: + + * :doc:`Templating and Shared Options ` + * :doc:`Creating JavaScript Callback Functions ` + +---------------- + +****************************************** +Rendering the Chart +****************************************** + +Once we've assembled and configured our chart, we can render it. How we want +to render it depends on our exact needs. We can: + + * Display the chart in a `Jupyter Notebook `__ + * Save the chart as a static image + * Generate the JavaScript that will render the chart in your users' + web browser. + +Displaying in Jupyter Notebook / Jupyter Lab +=============================================== + +If you're using **Highcharts Core for Python** in a Jupyter Notebook or Jupyter Lab, +you can display the chart right in your notebook. Doing so is super simple - just +call the :meth:`.display() ` method on +``my_chart``: + + .. code-block:: python + + my_chart.display() + +That's it! The chart will now render in the output of the cell. + + .. seealso:: + + * :doc:`Using Highcharts Core for Python with Jupyter ` + +Saving the Chart as a Static Image +======================================= + +If you want to save the chart as a static image, you can do so by calling the +:meth:`.download_chart() ` method: + + .. code-block:: python + + my_chart.download_chart(filename = 'my_chart.png') + +If you need it in a different format, you can specify the format using the +``format`` parameter. Highcharts for Python supports PNG (the default), +JPEG, PDF, and SVG. For example, to save the chart as a PDF, you can do: + + .. code-block:: python + + my_chart.download_chart(filename = 'my_chart.pdf', format = 'pdf') + +And that's it! + + .. seealso:: + + * :doc:`Exporting Static Charts ` + +Rendering in the Web Browser +================================ + +If you want to render your chart in your user's web browser, then you can +use **Highcharts for Python** to automatically generate the JavaScript code +you will need. The best way to do this is to call the +:meth:`.to_js_literal() ` method +on ``my_chart``. + +This will produce a string (or write to a file) containing the +:term:`JS literal ` form of your chart and +its configuration. If the code contained in this string gets executed in your +user's browser (within a set of ```` tags), it will render +your chart. + +So the way to get the JS literal is very straightforward: + + .. code-block:: python + + # EXAMPLE 1. + # Storing the JS literal in a string. + + my_js_literal = my_chart.to_js_literal() + + # EXAMPLE 2. + # Saving the JS literal to a file named + # "my-js-literal.js" + + my_chart.to_js_literal('my-js-literal.js') + +Now the way to render this chart will ultimately depend on how your application +is architected. We see three - typical - patterns employed: + + #. If your Python code is responsible for preparing the client-side + HTML + JavaScript, then you can include ``my_js_literal`` in your + template file. This pattern works for practically every Python web + framework, including `Django `__, + and `Flask `__. + #. If your Python code exposes RESTful or GraphQL APIs that are + consumed by your client-side application, then you can return the JS + literal object as a string via your API. This can then be evaluated + in your client-side application using JavaScript's ``new Function()`` + feature. + + .. caution:: + + **DO NOT USE JAVASCRIPT'S eval() FUNCTION**. + + It is deprecated, and for good reason: + + It represents a major security risk. When using ``new Function()``, + we recommend balancing the need for simplicity against the need for + security. You can secure your code by applying whitelisting techniques, + sandboxing the scope of your ``new Function()`` context, adding + additional layers of M2M signed encryption, or employing sanitization + techniques on the content of the JS literal returned by your API. + + Which specific techniques make sense will depend on your application + and your use case. + + #. If the data in your front-end application is generated on a periodic / batch + basis, then you can save your JS literal to a static file (saved where + consumable by your front-end application) and have your front-end application + simply load it as-needed. \ No newline at end of file diff --git a/docs/tutorials/pandas.rst b/docs/tutorials/pandas.rst new file mode 100644 index 0000000..cc9589f --- /dev/null +++ b/docs/tutorials/pandas.rst @@ -0,0 +1,498 @@ +######################################################## +Using Highcharts Core for Python with Pandas +######################################################## + +.. contents:: + :depth: 2 + :backlinks: entry + +------------------- + +`Pandas `__ is probably the single +most popular library for data analysis in the Python ecosystem. Together +with `NumPy `__, it is ubquitous. And the +**Highcharts for Python Toolkit** is designed to natively integrate with +it. + +.. sidebar:: What Can Pandas Do in Highcharts? + + The **Highcharts for Python Toolkit** is a *visualization* library. It is + not used to perform *arithmetical*, *algebraic*, *statistical*, or *matrix* + calculations. It is exclusively concerned with visualizing data - and that + data is typically the product of calculations that are performed up-stream. + + So given that focus, **Highcharts for Python** is designed to *read* data + that is contained in :class:`pandas.DataFrame ` + instances, or in :class:`numpy.ndarray ` instances. The + toolkit will take those instances and load the data they contain into + appropriate **Highcharts for Python** classes, and then support the + serialization of that data to + :term:`JS literal notation `. + +********************************* +General Approach +********************************* + +The **Highcharts for Python Toolkit** provides a number of standard methods +that are used to interact with :class:`pandas.DataFrame ` +instances. These methods generally take the form: + + * ``.from_pandas(df)`` This is always a class method which produces one or more + instances, with data pulled from the ``df`` argument. + * ``.from_pandas_in_rows(df)`` This is always a class method which produces one + instance for every *row* in the :class:`DataFrame ` + (``df``). + * ``.load_from_pandas(df)`` This is an instance method which updates an instance + with data read from the ``df`` argument. + +.. tip:: + + All three of these standard methods are packaged to have batteries included. + This means that for simple use cases, you can simply pass a + :class:`pandas.DataFrame ` to the method, and the + method wlil attempt to determine the optimum way to deserialize the + :class:`DataFrame ` into the appropriate + **Highcharts for Python** objects. + + However, if you find that you need more fine-grained control, the methods + provide powerful tools to give you the control you need when you need it. + +These standard methods - with near-identical syntax - are available: + + * On all :term:`series` classes (descended from + :class:`SeriesBase `) + * On the :class:`Chart ` class + * On the :class:`options.data.Data ` + class + +------------------------------- + +***************************** +Preparing Your DataFrame +***************************** + + .. tip:: + + While it is theoretically possible for **Highcharts for Python** to + work with a nested :class:`DataFrame `, such + structures are generally considered an anti-pattern. We recommend + keeping your :class:`DataFrame ` contents + 2-dimensional, organized into a single "flat" table of rows and + columns. + +So let's try a real-world example. Let's say you've got some annual population +counts stored in a CSV file named ``'census-time-series.csv'``. Using Pandas, you +can construct a DataFrame from that CSV file very simply: + + .. code-block:: python + + df = pandas.read_csv('census-time-series.csv') + +This produces a simple 2-dimensional :class:`DataFrame `. +In our case, the resulting table looks like this: + + .. image:: /_static/tutorials/census-time-series-01.png + :width: 100% + :alt: Rendering of the DataFrame produced by pandas.read_csv('census-time-series.csv') + +The first column contains the names of geographic regions, while each of the subsequent +columns contains the population counts for a given year. However, you'll notice that the +:class:`DataFrame ` index is not set. Unless told otherwise, +**Highcharts for Python** will look for x-axis values in the index. + +Secondly, if you were to look under the hood, you'd see that the +:class:`DataFrame ` imported all of the numbers in our CSV as +strings (because of the presence of the comma), which is obviously a bit of a problem. So +let's fix both of these issues: + + .. code-block:: python + + df = pandas.read_csv('census-time-series.csv', index_col = 0, thousands = ',')) + +produces: + + .. image:: /_static/tutorials/census-time-series-02.png + :width: 100% + :alt: Rendering of the DataFrame produced by pandas.read_csv('census-time-series.csv') + +Great! Now, let's say we wanted to visualize this data in various ways. + +------------------------------ + +********************************************* +Creating the Chart: ``Chart.from_pandas()`` +********************************************* + +Relying on the Defaults +=========================== + +The simplest way to create a chart from a :class:`DataFrame ` +is to call :class:`Chart.from_pandas() ` like +so: + +.. code-block:: python + + my_chart = Chart.from_pandas(df) + my_chart.display() + +.. image:: /_static/tutorials/census-time-series-03.png + :width: 100% + :alt: Rendering of the chart produced by Chart.from_pandas(df) + +As you can see, we haven't provided any more instructions besides telling it to +generate a chart from ``df``. The result is a line chart, with one series for each year, and +one point for each region. But because of the structure of our data file, this isn't a great chart: +all the series are stacked on each other! So let's fix that. + + .. tip:: + + Unless instructed otherwise, **Highcharts for Python** will default to using a + line chart. + +Setting the Series Type +============================== + +Why don't we switch it to a bar chart? + +.. list-table:: + :widths: 30 70 + +.. code-block:: python + + my_chart = Chart.from_pandas(df, series_type = 'bar') + my_chart.display() + +.. image:: /_static/tutorials/census-time-series-04.png + :width: 100% + :alt: Rendering of the chart produced by Chart.from_pandas(df, series_type = 'bar') + +Now the result is a little more readable, but still not great: After all, there are more than +fifty geographic regions represented for each year, which makes the chart super crowded. +Besides, maybe we're only interested in a specific year: 2019. + +Let's try focusing our chart. + +Basic Property Mapping +========================== + +.. code-block:: python + + my_chart = Chart.from_pandas(df, + series_type = 'bar', + property_map = { + 'x': 'Geographic Area', + 'y': '2019' + }) + +.. image:: /_static/tutorials/census-time-series-05.png + :width: 100% + :alt: Rendering of the chart produced by Chart.from_pandas(df, series_type = 'bar', property_map = {'x': 'Geographic Area', 'y': '2019'}) + +Much better! We've now added a ``property_map`` argument to the ``.from_pandas()`` method call. +This argument tells Highcharts for Python how to map columns in your +:class:`DataFrame ` to properties in the resulting chart. In this case, +the keys ``'x'`` and ``'y'`` tell **Highcharts for Python** that you want to map the ``'Geographic Area'`` +column to the resulting series' data points' :meth:`.x `, +and to map the ``'2019'`` column to the :meth:`.y ` +properties, respectively. + +The net result is that ``my_chart`` contains one +:class:`BarSeries ` whose +:meth:`.data ` property contains a +:class:`BarDataCollection ` instance populated +with the data from the ``'Geographic Area'`` and ``'2019'`` columns in ``df`` - and even though +``'Geographic Area'`` is not technically a column, but instead is used as the index, +**Highcharts for Python** still uses it correctly. + +But maybe we actually want to compare a couple different years? Let's try that. + +Property Mapping with Multiple Series +======================================== + +.. code-block:: python + + my_chart = Chart.from_pandas(df, + series_type = 'column', + property_map = { + 'x': 'Geographic Area', + 'y': ['2017', '2018', '2019'] + }) + my_chart.display() + +.. image:: /_static/tutorials/census-time-series-06.png + :width: 100% + :alt: Rendering of the chart produced by Chart.from_pandas(df, series_type = 'bar', property_map = {'x': 'Geographic Area', 'y': ['2017', '2018', '2019']}) + +Now we're getting somewhere! First, we changed our series type to a :class:`ColumnSeries ` to make it (a little) easier to read. Then we added a list of column names to the ``'y'`` key in the ``property_map`` argument. Each of those columns has now produced a *separate* :class:`ColumnSeries ` instance - but they're all still sharing the ``'Geographic Area'`` column as their :meth:`.x ` value. + + .. note:: + + You can supply multiple values to any property in the ``property_map``. The example + provided above is equivalent to: + + .. code-block:: python + + my_chart = Chart.from_pandas(df, + series_type = 'column', + property_map = { + 'x': ['Geographic Area', 'Geographic Area', 'Geographic Area'], + 'y': ['2017', '2018', '2019'] + }) + + The only catch is that the ultimate number of values for each key must match. If there's + only one value, then it will get repeated for all of the others. But if there's a mismatch, + then **Highcharts for Python** will throw a + :exc:`HighchartsPandasDeserializationError `. + +But so far, we've only been using the ``'x'`` and ``'y'`` keys in our ``property_map``. What if we wanted to +configure additional properties? Easy! + +Configuring Additional Properties +===================================== + +.. code-block:: python + + my_chart = Chart.from_pandas(df, + series_type = 'column', + property_map = { + 'x': 'Geographic Area', + 'y': ['2017', '2018', '2019'], + 'id': 'some other column' + }) + +Now, our data frame is pretty simple does not contain a column named ``'some other column'`. But *if* it did, +then it would use that column to set the :meth:`.id ` property of each data point. + + .. note:: + + You can supply any property you want to the ``property_map``. If the property is not + supported by the series type you've selected, then it will be ignored. + +But our chart is still looking a little basic - why don't we tweak some series configuration options? + +Configuring Series Options +=============================== + +.. code-block:: python + + my_chart = Chart.from_pandas(df, + series_type = 'column', + property_map = { + 'x': 'Geographic Area', + 'y': ['2017', '2018', '2019'], + }, + series_kwargs = { + 'point_padding': 5 + }) + my_chart.display() + +.. image:: /_static/tutorials/census-time-series-07.png + :width: 100% + :alt: Rendering of the chart produced by Chart.from_pandas(df, series_type = 'bar', property_map = {'x': 'Geographic Area', 'y': ['2017', '2018', '2019'], 'id': 'Geographic Area'}, series_kwargs = {'point_padding': 0.5}) + +As you can see, we supplied a new ``series_kwargs`` argument to the ``.from_pandas()`` method call. This +argument receives a :class:`dict ` with keys that correspond to properties on the series. In +this case, by supplying ``'point_padding'`` we have set the resulting +:meth:`ColumnSeries.point_padding ` property to a +value of ``0.5`` - leading to a bit more spacing between the bars. + +But our chart is *still* a little basic - why don't we give it a reasonable title? + +Configuring Options +============================= + +.. code-block:: python + + my_chart = Chart.from_pandas(df, + series_type = 'column', + property_map = { + 'x': 'Geographic Area', + 'y': ['2017', '2018', '2019'], + }, + series_kwargs = { + 'point_padding': 0.5 + }, + options_kwargs = { + 'title': { + 'text': 'This Is My Chart Title' + } + }) + my_chart.display() + +.. image:: /_static/tutorials/census-time-series-08.png + :width: 100% + :alt: Rendering of the chart produced by Chart.from_pandas(df, series_type = 'bar', property_map = {'x': 'Geographic Area', 'y': ['2017', '2018', '2019'], 'id': 'Geographic Area'}, series_kwargs = {'point_padding': 0.25}, options_kwargs = {'title': {'text': 'This Is My Chart Title'}}) + +As you can see, we've now given our chart a title. We did this by adding a new ``options_kwargs`` argument, +which likewise takes a :class:`dict ` with keys that correspond to properties on the chart's +:class:`HighchartsOptions ` configuration.` + +Now let's say we wanted our chart to render in an HTML ``
`` with an ``id`` of ``'my_target_div`` - +we can configure that in the same method call. + +Configuring Chart Settings +============================== + +.. code-block:: python + + my_chart = Chart.from_pandas(df, + series_type = 'bar', + property_map = { + 'x': 'Geographic Area', + 'y': ['2017', '2018', '2019'], + 'id': 'Geographic Area' + }, + series_kwargs = { + 'point_padding': 0.25 + }, + options_kwargs = { + 'title': { + 'text': 'This Is My Chart Title' + } + }, + chart_kwargs = { + 'container': 'my_target_div' + }) + +While you can't really *see* the difference here, by adding the ``chart_kwargs`` argument to +the method call, we now set the :meth:`.container ` property +on ``my_chart``. + +But maybe we want to do something a little different - like compare the change in population over time. +Well, we can do that easily by visualizing each *row* of ``df`` rather than each *column*.` + +Visualizing Data in Rows +============================== + +.. code-block:: python + + my_chart = Chart.from_pandas(df, + series_type = 'line', + series_in_rows = True) + my_chart.display() + +.. image:: /_static/tutorials/census-time-series-09.png + :width: 100% + :alt: Rendering of the chart produced by Chart.from_pandas(df, series_type = 'line', series_in_rows = True) + +Okay, so here we removed some of the other arguments we'd been using to simplify the example. You'll see we've now +added the ``series_in_rows`` argument, and set it to ``True``. This tells **Highcharts for Python** that we expect +to produce one series for every *row* in ``df``. Because we have not specified a ``property_map``, the series +:meth:`.name ` values are populated from the ``'Geographic Area'`` +column, while the data point :meth:`.x ` values come from each additional column (e.g. ``'2010'``, ``'2011'``, ``'2012'``, etc.) + + .. tip:: + + To simplify the code further, any class that supports the ``.from_pandas()`` method also + supports the ``.from_pandas_in_rows()`` method. The latter method is equivalent to passing + ``series_in_rows = True`` to ``.from_pandas()``. + + For more information, please see: + + * :class:`Chart.from_pandas_in_rows() ` + * :class:`SeriesBase.from_pandas_in_rows() ` + +But maybe we don't want *all* geographic areas shown on the chart - maybe we only want to compare a few. + +Filtering Rows +======================= + +.. code-block:: python + + my_chart = Chart.from_pandas(df, + series_type = 'line', + series_in_rows = True, + series_index = slice(7, 10)) + +.. image:: /_static/tutorials/census-time-series-10.png + :width: 100% + :alt: Rendering of the chart produced by Chart.from_pandas(df, series_type = 'line', series_in_rows = True, series_index = slice(7, 10)) + +What we did here is we added a ``series_index`` argument, which tells **Highcharts for Python** to only +include the series found at that index in the resulting chart. In this case, we supplied a :func:`slice ` +object, which operates just like ``list_of_series[7:10]``. The result only returns those series between index 7 and 10. + +------------------------ + +********************************************************************** +Creating Series: ``.from_pandas()`` and ``.from_pandas_in_rows()`` +********************************************************************** + +All **Highcharts for Python** :term:`series` descend from the +:class:`SeriesBase ` class. And they all +therefore support the ``.from_pandas()`` class method. + +When called on a series class, it produces one or more series from the +:class:`DataFrame ` supplied. The method supports all of the same options +as :class:`Chart.from_pandas() ` *except for* ``options_kwargs`` and +``chart_kwargs``. This is because the ``.from_pandas()`` method on a series class is only responsible for +creating series instances - not the charts. + +Creating Series from Columns +================================== + +So let's say we wanted to create one series for each of the years in ``df``. We could that like so: + + .. code-block:: python + + my_series = BarSeries.from_pandas(df) + +Unlike when calling :meth:`Chart.from_pandas() `, we +did not have to specify a ``series_type`` - that's because the ``.from_pandas()`` class method on a +series class already knows the series type! + +In this case, ``my_series`` now contains ten separate :class:`BarSeries ` +instances, each corresponding to one of the year columns in ``df``. + +But maybe we wanted to create our series from rows instead? + +Creating Series from Rows +================================= + +.. code-block:: python + + my_series = LineSeries.from_pandas_in_rows(df) + +This will produce one :class:`LineSeries ` +instance for each row in ``df``, ultimately producing a :class:`list ` of +57 :class:`LineSeries ` instances. + +Now what if we don't need all 57, but instead only want the first five? + +Filtering Series Created from Rows +====================================== + +.. code-block:: python + + my_series = LineSeries.from_pandas_in_rows(df, series_index = slice(0, 5)) + +This will return the first five series in the list of 57. +-------------------------- + +*********************************************************** +Updating an Existing Series: ``.load_from_pandas()`` +*********************************************************** + +So far, we've only been creating new series and charts. But what if we want to update +the data within an existing series? That's easy to do using the +:meth:`.load_from_pandas() ` method. + +Let's say we take the first series returned in ``my_series`` up above, and we want to replace +its data with the data from the *10th* series. We can do that by: + + .. code-block:: python + + my_series[0].load_from_pandas(df, series_in_rows = True, series_index = 9) + +The ``series_in_rows`` argument tells the method to generate series per row, and then +the ``series_index`` argument tells it to only use the 10th series generated. + + .. caution:: + + While the :meth:`.load_from_pandas() ` + method supports the same arguments as + :meth:`.from_pandas() `, it expects that + the arguments supplied lead to an unambiguous *single* series. If they are ambiguous - meaning they + lead to multiple series generated from the :class:`DataFrame ` - then + the method will throw a + :exc:`HighchartsPandasDeserializationError ` \ No newline at end of file diff --git a/docs/tutorials/series.rst b/docs/tutorials/series.rst new file mode 100644 index 0000000..129ac61 --- /dev/null +++ b/docs/tutorials/series.rst @@ -0,0 +1,398 @@ +################################################### +Working with Series in Highcharts for Python +################################################### + +.. contents:: + :depth: 2 + :backlinks: entry + +------------------- + +**Highcharts for Python** (and Highcharts (JS), of course) are both built around +the concept of :term:`data series `. A data series can be thought of as +a set of data points that describe the same "thing", and which represent how +the data can be organized. Think: + + * a single line on a line chart + * a set of columns all the same color on a column chart + * all of a pie or donut chart + +As a result, when you constructing your chart in **Highcharts for Python**, +what you are really doing is constructing one or more :term:`series` that +are then placed on a shared canvas, with shared axes, a shared legend, etc. + + .. image:: /_static/highcharts-chart-anatomy.png + :width: 75 + :align: right + :alt: Diagram showing the conceptual components of a Highcharts chart + +This tutorial is designed to help you understand how to manage series in your +Highcharts visualizations using **Highcharts for Python**. + +---------------------------------- + +********************************* +How Series are Organized +********************************* + +As the diagram above shows, Highcharts visualizations are configured +within a :class:`Chart ` object, which has +the :meth:`.options ` property where +the chart's configuration "lives", represented as a +:class:`HighchartsOptions ` +instance. + +The :class:`HighchartsOptions ` +configuration allows you to define *all* details of what gets displayed on +the chart, how that content behaves, etc. The configuration options available in +:class:`HighchartsOptions ` can be +thought of as chart-level configurations: They configure or define things that apply to +everything shown on the chart: the axes to display, the legend, the title, the settings shared +across all of the series on the chart, etc. + +But within the :class:`HighchartsOptions ` +you will find the :meth:`.series ` +property. This is where you define the *specific* :term:`series` to render on your chart. + +This property gets one or more series instances, all of which are descended from +:class:`SeriesBase `. They descend from +this single base series class because many of their properties are shared across all types +of series. For example, whether the series is to render as a line or as a bar, all series will +have an :meth:`.id ` option. + +All visualizations supported by Highcharts have a corresponding series type, which means +they all have their corresponding series class. To see this mapping, take a look at our +:doc:`Supported Visualizations `. + +Each series type has its set of shared properties that derive from +:class:`SeriesBase , but specific series types +may have their own type-specific settings. For example, a +:class:`GaugeSeries ` will have options to +configure the gauge's dial (:meth:`.dial `), +overshot-handling (:meth:`.overshoot `), +and pivot point (:meth:`.pivot `) - settings +which would be completely *irrelevant* for a +:class:`LineSeries `, which does not have a dial, +does not have a concept of overshooting the bounds of the dial, and does not have any pivot points. + +.. list-table:: + :widths: 50 50 + + * - .. figure:: ../../../_static/gauge-example.png + :alt: Gauge Example Chart + :align: center + - .. figure:: ../../../_static/line-example.png + :alt: Line Example Chart + :align: center + +And all series (technically, *almost* all) have a +:meth:`.data ` property, which contains the +data that should be visualized in the series. + +.. sidebar:: Exceptions without Data + + Certain types of visualiation - like + :class:`BellCurveSeries ` + do not receive their own data, but instead are tied to an underlying + :meth:`.base_series ` + whose data is used to shape their visualization. + + .. figure:: ../../../_static/bellcurve-example.png + :alt: Bell Curve Example Chart + :align: center + :width: 35% + +So as you can see, series are pretty fundamental to your Highcharts visualizations: They are +what actually gets visualized. + +So how do we start creating series using **Highcharts for Python**? + +--------------------------------------- + +*************************************************** +Creating Series in Highcharts for Python +*************************************************** + + +Of course, you can always construct your series using direct instantiation: + + .. code-block:: python + + from highcharts_gantt.chart import Chart + from highcharts_gantt.options import HighchartsOptions + from highcharts_gantt.options.series.area import LineSeries + + my_line_series = LineSeries(data = my_data, id = 'my_series_id123') + my_options = HighchartsOptions(series = [my_line_series]) + my_chart = Chart(options = my_options) + +And there may be situations where there is the best way for you to construct your +series, depending on how you are managing your full Highcharts for Python application. + +But there are much simpler / faster ways to rapidly create your chart/series: + +Assembling a Chart with Series at Once +========================================= + +.. include:: _assembling_a_chart.rst + +Assembling Series Alone +========================== + +You can create series similarly: + + .. code-block:: python + + from highcharts_gantt.options.series.area import LineSeries + + my_line_series = LineSeries(data = my_iterable) + +This will create a :class:`LineSeries ` +instance. + +Depending on how we've wrangled our data, we can similarly produce one or more +series from a :class:`pandas.DataFrame `, +:class:`numpy.ndarray `, or Python :class:`dict `: + + .. code-block:: python + + # From a Pandas DataFrame + + my_series = LineSeries.from_pandas(df) + + # From a Numpy ndarray + + my_series = LineSeries.from_array(data = as_ndarray) + + # From a CSV file + + my_series = LineSeries.from_csv('my-data.csv') + + # From a Python iterable + + my_series = LineSeries.from_array(data = as_iterable) + +All of these lines of code are equivalent, and should produce an identical +``my_series``. Depending on the arguments you supply to the helper methods, +they may either produce *one* series instance or a :class:`list ` +of series instances. + +Adding Series to a Chart +=========================== + +If you have created series on their own, you can add them to an existing +:class:`Chart ` very easily: + + .. code-block:: python + + # EXAMPLE 1. + # Adding one series + + my_chart.add_series(my_series) + + # EXAMPLE 2. + # Adding multiple series if they are in one iterable + + my_chart.add_series(my_list_of_series) + + # EXAMPLE 3. + # Adding multiple individual series + + my_chart.add_series(series1, series2, series3) + +Or you can also create a new chart *from* a list of series: + + .. code-block:: python + + # EXAMPLE 1. + # With one series + + my_chart = Chart.from_series(my-series) + + # EXAMPLE 2. + # With multiple series if they are in one iterable + + my_chart = Chart.from_series(my_list_of_series) + + # EXAMPLE 3. + # Adding multiple individual series + + my_chart = Chart.from_series(series1, series2, series3) + +.. tip:: + + The same :meth:`.add_series ` and + :meth:`.from_series ` helper methods + are also available on the + :class:`HighchartsOptions ` class. + +So now that we've created a chart and a bunch of series, what else can we do? + +---------------------------- + +********************************* +Configuring Your Series +********************************* + +You can configure the options available on each series very simply. Highcharts (JS) - +and so **Highcharts for Python** - have a very extensive API, with lots of configuration +options. + +.. tip:: + + We recommend reviewing the :doc:`API Reference ` to really explore + the options available for different series types. + +Updating Data Points +========================= + +However, the most important configuration you will do is to manage the data points +you wish to display in your series. You can do this by: + + #. Passing data directly to the + :meth:`.data ` + property: + + .. code-block:: python + + my_series.data = updated_data + + #. Using any of the helper methods provided on the series instance: + + .. code-block:: python + + # EXAMPLE 1. + # Updating data points from a new Pandas DataFrame + + my_series.load_from_pandas(df) + + # EXAMPLE 2. + # Updating data points from a new numpy.ndarray + + my_series.load_from_array(as_ndarray) + + # EXAMPLE 3. + # Updating data points from a new iterable. + + my_series.load_from_array(as_iterable) + + # EXAMPLE 4. + # Updating data points from a CSV file. + + my_series.load_from_csv('my-updated-csv-file.csv') + +Updating Data Point Properties +================================== + +In addition, all series instances make it easy to propagate information throughout your +underlying data. When you try to set a property on your series object, **Highcharts for Python** +will first see if it is a valid property on the series itself. But if it is not, then it will +check whether it is a supported property on that series' *data*. + +So as an example, if we want to give our series an ID, we can simply call: + + .. code-block:: python + + my_series.id = 'my-updated-id-value' + +However, if we want to set a :class:`BulletSeries ` data points' :meth:`.target ` value, we can simply reference it on the series. +For example, if our :class:`BulletSeries ` contains three data points, we can set their targets easily using the series: + + .. code-block:: python + + my_bullet_series.target = [1, 2, 3] + +By passing an iterable (or a :class:`numpy.ndarray `), *all* of your data +points will get updated with the appropriate value. This makes it very easy to execute your data point +configurations by operating on the series, rather than working with *individual* data points - though if you want +to work with *individual* data points, you can do so as well. + +.. seealso:: + + * :doc:`Working with Data in Highcharts for Python ` + +Converting Series Types +=========================== + +Every series type has its own type-specific set of configuration options. However, there may be +times when you want to change how your data is to be visualized / rendered. **Highcharts for Python** +provides a useful helper method for that, too. For example, if we want to convert our +:class:`LineSeries ` to a +:class:`BarSeries `, we can do that by calling the +:meth:`.convert_to ` method: + + .. code-block:: python + + # EXAMPLE 1 + # Indicating the target type with a string label + + my_series.convert_to(series_type = 'bar') + + # EXAMPLE 2 + # Indicating the target type with a SeriesBase class + + my_series.convert_to(series_type = BarSeries) + +So now that we've constructed, configured, and adjusted our series, we can also render them +easily. + +----------------------- + +*************************** +Rendering Series +*************************** + +Series can be rendered within the chart that they are a part of, simply by following +the process to render the chart: + +Rendering a Series within a Chart +======================================= + + #. When in Jupyter Notebook/Lab, just execute the :meth:`.display() ` + method. + + .. code-block:: python + + my_chart.display() + + #. When rendering within a web application, or saving to a file for rendering in a separate application, + you can serialize your chart to :term:`JavaScript object literal notation`: + + .. code-block:: python + + as_js_literal = my_chart.to_js_literal() + + which will produce the JavaScript code to render your complete chart. + +Rendering a Series Alone +============================ + +The *exact same* helper methods are available on your series as well. So if you have assembled your +series as ``my_series``, you can take a shortcut to visualize it by calling: + + .. code-block:: python + + my_series.display() + +which will assemble a generic :class:`Chart ` instance, include +your series, and render it in Jupyter Notebook/Lab. + +You can also produce a :class:`Chart ` instance containing your +series in a single method call as well: + + .. code-block:: python + + my_chart = my_series.to_chart() + +And similarly: + + .. code-block:: + + series_as_js_literal = my_series.to_js_literal() + +will produce the JS literal representation of your series, for use in a JavaScript application. + +------------ + +Given all of this flexibliity, we hope you have a great time assembling +high-end visualizations and exploring **Highcharts for Python**! \ No newline at end of file diff --git a/docs/using.rst b/docs/using.rst index 34757ad..8b4d9b6 100644 --- a/docs/using.rst +++ b/docs/using.rst @@ -86,7 +86,6 @@ more Pythonic way of interacting with the framework. Here are the notable design patterns that have been adopted that you should be aware of: - Code Style: Python vs JavaScript Naming Conventions ======================================================= @@ -95,9 +94,9 @@ Code Style: Python vs JavaScript Naming Conventions Standard Methods ======================================= -Every single object supported by the Highcharts Gantt API corresponds to a Python class in -**Highcharts Gantt for Python**. You can find the complete list in our comprehensive -:doc:`Highcharts Gantt for Python API Reference `. +Every single object supported by the Highcharts JavaScript API corresponds to a Python +class in **Highcharts Gantt for Python**. You can find the complete list in our +comprehensive :doc:`Highcharts Gantt for Python API Reference `. These classes generally inherit from the :class:`HighchartsMeta ` metaclass, which @@ -148,18 +147,18 @@ Class Structures and Inheritance .. warning:: - Certain sections of the **Highcharts Maps for Python** library - in particular the - :mod:`options.series ` classes - rely heavily on + Certain sections of **Highcharts Gantt for Python** - in particular the + :mod:`options.series ` classes - rely heavily on multiple inheritance. This is a known anti-pattern in Python development as it runs the risk of encountering the :term:`diamond of death` inheritance problem. This complicates the process of inheriting methods or properties from parent classes when properties or methods share names across multiple parents. - We know the diamond of death is an anti-pattern, but it was a necessary one to minimize - code duplication and maximize consistency. For that reason, we implemented it properly - *despite* the anti-pattern, using some advanced Python concepts to navigate the Python MRO - (Method Resolution Order) system cleanly. However, an awareness of the pattern used - may prove helpful if your code inherits from the Highcharts for Python classes. + We know the diamond of death is an anti-pattern, but it was a necessary one to + minimize code duplication and maximize consistency. For that reason, we implemented it + properly *despite* the anti-pattern, using some advanced Python concepts to navigate the + Python MRO (Method Resolution Order) system cleanly. However, an awareness of the pattern + used may prove helpful if your code inherits from the Highcharts for Python classes. .. seealso:: @@ -366,88 +365,164 @@ Jira projects, CSV files, from `pandas`_ dataframes, or `PySpark`_ dataframes. How Data is Represented ================================== -`Highcharts `__ (JS) supports two different ways of representing -data: as an individual :term:`series` comprised of individual data points, and as a set of -instructions to read data dynamically from a CSV file or an HTML table. +`Highcharts `__ (JS) supports two different ways of +representing data: as an individual :term:`series` comprised of individual data +points, and as a set of instructions to read data dynamically from a CSV file or +an HTML table. .. seealso:: * :class:`DataBase ` class * :class:`options.Data ` class -`Highcharts `__ organizes data into :term:`series`. You can -think of a series as a single line on a graph that shows a set of values. The set of -values that make up the series are :term:`data points `, which are defined by -a set of properties that indicate the data point's position on one or more axes. +`Highcharts `__ organizes data into :term:`series`. You +can think of a series as a single line on a graph that shows a set of values. The set +of values that make up the series are :term:`data points `, which are defined +by a set of properties that indicate the data point's position on one or more axes. -As a result, `Highcharts (JS) `__ and **Highcharts for Python** both -represent the data points in series as a list of data point objects in the ``data`` property -within the series: +As a result, `Highcharts (JS) `__ and +**Highcharts for Python** both represent the data points in series as a list of data point +objects in the ``data`` property within the series: -.. list-table:: - :widths: 50 50 - :header-rows: 1 + .. list-table:: + :widths: 50 50 + :header-rows: 1 - * - Highcharts JS - - Highcharts for Python - * - .. code-block:: javascript + * - Highcharts (JS) + - Highcharts for Python + * - .. code-block:: javascript - // Example Series Object - // (for a Line series type): - { - data: [ - { - id: 'first-data-point', - x: 1, - y: 123, - // ... - // optional additional properties - // for styling/behavior go here - // ... - }, + // Example Series Object + // (for a Line series type): { - id: 'second-data-point', - x: 2, - y: 456, - // ... - // optional additional properties - // for styling/behavior go here + data: [ + { + id: 'first-data-point', + x: 1, + y: 123, + // ... + // optional additional properties + // for styling/behavior go here + // ... + }, + { + id: 'second-data-point', + x: 2, + y: 456, + // ... + // optional additional properties + // for styling/behavior go here + // ... + }, + { + id: 'third-data-point', + x: 3, + y: 789, + // ... + // optional additional properties + // for styling/behavior go here + // ... + } + ], // ... - }, + // other Series properties go here + // to configure styling/behavior + } + - .. code-block:: python + + # Corresponding LineSeries object + my_series = Series(data = [ + CartesianData(id = 'first-data-point1', + x = 1, + y = 123), + CartesianData(id = 'second-data-point1', + x = 2, + y = 456), + CartesianData(id = 'third-data-point1', + x = 3, + y = 789), + ]) + + #. As a single :class:`DataPointCollection ` + object in the ``data`` property within the series, which in turn contains the + individual data points. + + .. list-table:: + :widths: 50 50 + :header-rows: 1 + + * - Highcharts (JS) + - Highcharts for Python + * - .. code-block:: javascript + + // Example Series Object + // (for a Line series type): { - id: 'third-data-point', - x: 3, - y: 789, - // ... - // optional additional properties - // for styling/behavior go here + data: [ + { + name: 'first-data-point', + x: 1, + y: 123, + // ... + // optional additional properties + // for styling/behavior go here + // ... + }, + { + name: 'second-data-point', + x: 2, + y: 456, + // ... + // optional additional properties + // for styling/behavior go here + // ... + }, + { + name: 'third-data-point', + x: 3, + y: 789, + // ... + // optional additional properties + // for styling/behavior go here + // ... + } + ], // ... + // other Series properties go here + // to configure styling/behavior } - ], - // ... - // other Series properties go here - // to configure styling/behavior - } - - .. code-block:: python - - # Corresponding LineSeries object - my_series = Series(data = [ - CartesianData(id = 'first-data-point1', - x = 1, - y = 123), - CartesianData(id = 'second-data-point1', - x = 2, - y = 456), - CartesianData(id = 'third-data-point1', - x = 3, - y = 789), - ]) + - .. code-block:: python + + # EXAMPLE 1. Corresponding LineSeries object, with data as an + # numpy.ndarray. + + my_series = Series(data = CartesianDataCollection( + ndarray = [ + [1, 123, 'first-data-point1'], + [2, 456, 'second-data-point1'], + [3, 789, 'third-data-point1']) + ]) + + # EXAMPLE 2. Corresponding LineSeries object, with data as a + # primitive array. + + my_series = Series(data = CartesianDataCollection( + array = [ + [1, 123, 'first-data-point1'], + [2, 456, 'second-data-point1'], + [3, 789, 'third-data-point1']) + ] + )) As you can see, **Highcharts for Python** represents its data the same way that -`Highcharts (JS) `__ does. That should be expected. However, -constructing tens, hundreds, or possibly thousands of data points individually in your -code would be a nightmare. For that reason, the **Highcharts for Python** toolkit provides -a number of convenience methods to make it easier to populate your series. +`Highcharts (JS) `__ does. That should be expected. +However, constructing tens, hundreds, or possibly thousands of data points +individually in your code would be a nightmare. For that reason, the +**Highcharts for Python Toolkit** natively supports vectorized +:class:`numpy.ndarray ` values, and automatically assembles data +point collections for easy management/manipulation. In addition, the Toolkit +provides a number of convenience methods to make it easier to populate your +series. .. _populating_series_data: @@ -483,14 +558,17 @@ series instance), or to create a new series instance with data already loaded. :ref:`deserialization methods `, so those make things very easy. However, they also have a special data point-specific deserialization method: - .. collapse:: Method Signature + .. collapse:: Expand Method Signature .. method:: .from_array(cls, value) :classmethod: :noindex: - Creates a collection of data point instances, parsing the contents of ``value`` as an - array (iterable). This method is specifically used to parse data that is input to + Creates a + :class:`DataPointCollection ` + instance if possible, or falls back to a collection of data point + instances, parsing the contents of ``value`` as an array (iterable). This method + is specifically used to parse data that is input to **Highcharts for Python** without property names, in an array-organized structure as described in the `Highcharts (JS) `__ documentation. @@ -513,12 +591,37 @@ series instance), or to create a new series instance with data already loaded. my_series = LineSeries() + # EXAMPLE 1. + # A simple array of numerical values which correspond to the Y value of the data + # point, passed to the .from_array() method. + + my_series = LineSeries.from_array([0, 5, 3, 5]) + + # EXAMPLE 2. # A simple array of numerical values which correspond to the Y value of the data - # point + # point, passed to the series data property. + my_series.data = [0, 5, 3, 5] + # EXAMPLE 3. + # A simple array of numerical values which correspond to the Y value of the data + # point, updated in the series using the .load_from_array() method. + + my_series.load_from_array([0, 5, 3, 5]) + + # EXAMPLE 4. # An array containing 2-member arrays (corresponding to the X and Y values of the - # data point) + # data point), passed to the .from_array() class method. + my_series = LineSeries.from_array([ + [0, 0], + [1, 5], + [2, 3], + [3, 5] + ]) + + # EXAMPLE 5. + # An array containing 2-member arrays (corresponding to the X and Y values of the + # data point), passed to the series data property. my_series.data = [ [0, 0], [1, 5], @@ -526,7 +629,37 @@ series instance), or to create a new series instance with data already loaded. [3, 5] ] - # An array of dict with named values + # EXAMPLE 6. + # An array of dict with named values, passed to the .from_array() class method. + my_series = LineSeries.from_array([ + { + 'x': 0, + 'y': 0, + 'name': 'Point1', + 'color': '#00FF00' + }, + { + 'x': 1, + 'y': 5, + 'name': 'Point2', + 'color': '#CCC' + }, + { + 'x': 2, + 'y': 3, + 'name': 'Point3', + 'color': '#999' + }, + { + 'x': 3, + 'y': 5, + 'name': 'Point4', + 'color': '#000' + } + ]) + + # EXAMPLE 6. + # An array of dict with named values, passed to the series data property. my_series.data = [ { 'x': 0, @@ -554,6 +687,36 @@ series instance), or to create a new series instance with data already loaded. } ] + # EXAMPLE 6. + # An array of dict with named values, passed to .load_from_array() + # method. + my_series.load_from_array([ + { + 'x': 0, + 'y': 0, + 'name': 'Point1', + 'color': '#00FF00' + }, + { + 'x': 1, + 'y': 5, + 'name': 'Point2', + 'color': '#CCC' + }, + { + 'x': 2, + 'y': 3, + 'name': 'Point3', + 'color': '#999' + }, + { + 'x': 3, + 'y': 5, + 'name': 'Point4', + 'color': '#000' + } + ]) + :param value: The value that should contain the data which will be converted into data point instances. @@ -568,13 +731,16 @@ series instance), or to create a new series instance with data already loaded. :class:`DataBase `) :rtype: :class:`list ` of :class:`DataBase `-descendant - instances + instances or + :class:`DataPointCollection ` + instance - .. tab:: Load to Existing Series + .. tab:: Update an Existing Series .. warning:: - :term:`Technical indicators ` do not support the ``.load_from_*`` methods because + :term:`Technical indicators ` provided by + **Highcharts Stock for Python** do not support the ``.load_from_*`` methods because their data gets populated dynamically based on the series indicated in their :meth:`.linked_to ` property. @@ -583,19 +749,12 @@ series instance), or to create a new series instance with data already loaded. * :doc:`Using Highcharts for Python ` > :ref:`Using Technical Indicators ` - .. tabs:: - - .. tab:: Using ``.load_from_asana()`` - - .. include:: using/populating_series_data/_load_from_asana.rst - .. tab:: Using ``.load_from_monday()`` - - .. include:: using/populating_series_data/_load_from_monday.rst + .. tabs:: - .. tab:: Using ``.load_from_jira()`` + .. tab:: Using ``.load_from_array()`` - .. include:: using/populating_series_data/_load_from_jira.rst + .. include:: using/populating_series_data/_load_from_array.rst .. tab:: Using ``.load_from_csv()`` @@ -613,8 +772,10 @@ series instance), or to create a new series instance with data already loaded. .. warning:: - :term:`Technical indicators ` do not support the ``.from_*()``, - methods because their data gets populated dynamically based on the series indicated in their + :term:`Technical indicators ` provided by + **Highcharts for Python** do not support the ``.from_csv()``, + ``.from_pandas()``, and ``.from_pyspark()`` methods because their data gets populated + dynamically based on the series indicated in their :meth:`.linked_to ` property. @@ -624,6 +785,10 @@ series instance), or to create a new series instance with data already loaded. .. tabs:: + .. tab:: Using ``.from_array()`` + + .. include:: using/populating_series_data/_new_from_array.rst + .. tab:: Using ``.from_asana()`` .. include:: using/populating_series_data/_new_from_asana.rst @@ -685,6 +850,10 @@ property. You can do this in several ways: .. tabs:: + .. tab:: Using Keyword Arguments + + .. include:: using/adding_series_to_charts/_using_kwargs.rst + .. tab:: Using ``.options.series`` .. include:: using/assembling_your_chart/_using_series_property.rst @@ -744,29 +913,41 @@ your visualizations: Rendering Highcharts Visualizations in Web Content ======================================================== -`Highcharts JS `__ is a JavaScript library specifically -designed to enable rendering high-end data visualizations in a web context. The library is -designed and optimized to operate within a web browser. The **Highcharts for Python** -toolkit therefore fully supports this capability, and we've enabled it using the -*batteries included* principle. +`Highcharts `__ is a suite of JavaScript libraries +designed to enable rendering high-end data visualizations in a web context. They are +designed and optimized to operate within a web browser. The +**Highcharts for Python Toolkit** therefore fully supports this capability, and we've +enabled it using the *batteries included* principle. -To render a **Highcharts Gantt for Python** visualization, all you need is for the browser -to execute the output of the chart's -:meth:`.to_js_literal() ` method, which will -return a snippet of JavaScript code which when included in a web page will display the -chart in full. +To render a **Highcharts for Python** visualization in a web context, all you need is +for the browser to execute the output of the chart's +:meth:`.to_js_literal() ` method. + +That method will return a snippet of JavaScript code which when included in a web page +will display the chart in full. .. warning:: - The current version of **Highcharts Gantt for Python** assumes that your web content - already has all the ``