Merge branch 'master' into cmake_build

This commit is contained in:
jkriege2 2019-06-20 10:28:53 +02:00
commit a390030db4
312 changed files with 23193 additions and 6724 deletions

View File

@ -1,17 +1,18 @@
TEMPLATE = subdirs
SUBDIRS += jkqtplotterlib \
jkqtplotterlib_sharedlib \
jkqtplotterlib_sharedlib \
jkqtmathtextlib \
jkqtmathtextlib_sharedlib \
jkqtpcommonlib \
jkqtpcommonlib_sharedlib \
jkqtfastplotterlib \
jkqtfastplotterlib_sharedlib \
jkqtphighrestimerlib \
jkqtmathtext_simpletest \
jkqtplot_test \
jkqtplotter_simpletest \
test_multiplot \
jkqtfastplotter_test
test_multiplot \
jkqtfastplotter_test
jkqtplotterlib.file = staticlib/jkqtplotterlib/jkqtplotterlib.pro
@ -20,13 +21,14 @@ jkqtplotterlib_sharedlib.file = sharedlib/jkqtplotterlib/jkqtplotterlib.pro
jkqtmathtextlib.file = staticlib/jkqtmathtextlib/jkqtmathtextlib.pro
jkqtmathtextlib_sharedlib.file = sharedlib/jkqtmathtextlib/jkqtmathtextlib.pro
jkqtpcommonlib.file = staticlib/jkqtpcommonlib/jkqtpcommonlib.pro
jkqtpcommonlib_sharedlib.file = sharedlib/jkqtpcommonlib/jkqtpcommonlib.pro
jkqtfastplotterlib.file = staticlib/jkqtfastplotterlib/jkqtfastplotterlib.pro
jkqtfastplotterlib_sharedlib.file = sharedlib/jkqtfastplotterlib/jkqtfastplotterlib.pro
jkqtphighrestimerlib.file = staticlib/jkqtphighrestimerlib/jkqtphighrestimerlib.pro
jkqtmathtext_simpletest.subdir = examples/jkqtmathtext_simpletest
jkqtmathtext_simpletest.depends = jkqtmathtextlib jkqtphighrestimerlib
jkqtmathtext_simpletest.depends = jkqtmathtextlib
jkqtmathtext_test.subdir = examples/jkqtmathtext_test
jkqtmathtext_test.depends = jkqtplotterlib
@ -78,6 +80,13 @@ addSimpleTest(boxplot)
addSimpleTest(advancedlineandfillstyling)
addSimpleTest(imageplot_nodatastore)
addSimpleTest(datastore)
addSimpleTest(datastore_iterators)
addSimpleTest(datastore_statistics)
addSimpleTest(datastore_statistics_2d)
addSimpleTest(datastore_regression)
addSimpleTest(datastore_groupedstat)
addSimpleTest(contourplot)
addSimpleTest(violinplot)
#addSimpleTest(rgbimageplot_opencv)
#addSimpleTest(imageplot_opencv)

View File

@ -21,6 +21,8 @@ This software is licensed under the term of the [GNU Lesser General Public Licen
- external or internal datasets
- complete with GUI (table view)
- export capabilities (e.g. to CSV, SYLK, ...)
- C++ standard iterator interface
- statistics library (basic statistics, boxplots, histograms, kernel density estimates, regression analysis, polynomial fitting)
- large variety of graphs that can be added to a plot, e.g.:
- scatter-plots (also parametrized color/size/symbol by a third data-column)
- line graphs, step graphs, impulses

View File

@ -62,6 +62,9 @@ All test-projects are Qt-projects that use qmake to build. You can load them int
<tr><td> \image html jkqtplotter_simpletest_boxplot_small.png
<td> \subpage JKQTPlotterBoxplotsGraphs
<td> `JKQTPBoxplotVerticalGraph`, `JKQTPBoxplotHorizontalGraph`, ...
<tr><td> \image html jkqtplotter_simpletest_violinplot_small.png
<td> \subpage JKQTPlotterViolinplotGraphs
<td> `JKQTPViolinplotVerticalElement`, `JKQTPViolinplotHorizontalElement`, ...
</table>
@ -113,6 +116,9 @@ All test-projects are Qt-projects that use qmake to build. You can load them int
<tr><td> \image html jkqtplotter_simpletest_rgbimageplot_opencv_small.png
<td> \subpage JKQTPlotterImagePlotRGBOpenCV
<td> `JKQTPColumnRGBMathImage` <br> image data copied from OpenCV cv::Mat-structure into three columns of the internal datastore <br> inverted coordinate axes
<tr><td> \image html jkqtplotter_simpletest_contourplot_small.png
<td> \subpage JKQTPlotterContourPlot
<td> `JKQTPColumnContourPlot` <br> image data edited inside JKQTPDatastore
</table>
@ -143,13 +149,28 @@ All test-projects are Qt-projects that use qmake to build. You can load them int
</table>
\subsection jkqtp_extut_datamanagement Data Management
\subsection jkqtp_extut_datamanagement Data Management & Statistics
<table>
<tr><th> Screenshot <th> Description <th> Notes
<tr><td> \image html simpletest_datastore_small.png
<td> \subpage JKQTPlotterBasicJKQTPDatastore
<td> Basic Data Management with JKQTPDatastore <br/> Copying data into a JKQTPDatastore <br/> Editing data inside a JKQTPDatastore <br/> Editing Image Data in a JKQTPDatastore
<tr><td> \image html simpletest_datastore_iterators_small.png
<td> \subpage JKQTPlotterBasicJKQTPDatastoreIterators
<td> Iterator-based Data Management with JKQTPDatastore
<tr><td> \image html jkqtplotter_simpletest_datastore_statistics_small.png
<td> \subpage JKQTPlotterBasicJKQTPDatastoreStatistics
<td> Advanced 1-Dimensional Statistical Computation with JKQTPDatastore<br>using the internal statistics library (see \ref jkqtptools_math_statistics )<br>basic statistics (mean, standard deviation, ...)<br>boxplots<br>histograms<br>kernel density estimates (KDE)
<tr><td> \image html jkqtplotter_simpletest_datastore_regression_small.png
<td> \subpage JKQTPlotterBasicJKQTPDatastoreRegression
<td> Advanced 1-Dimensional Statistical Computation with JKQTPDatastore<br>using the internal statistics library<br>Regression Analysis (with the Statistics Library)<br>robust regression (IRLS)<br>weighted regression<br>non-linear regression<br>polynomial fitting
<tr><td> \image html jkqtplotter_simpletest_datastore_groupedstat_small.png
<td> \subpage JKQTPlotterBasicJKQTPDatastoreStatisticsGroupedStat
<td> Advanced 1-Dimensional Statistical Computation with JKQTPDatastore<br>grouped statistics<br>error indicators from data<br>boxplots
<tr><td> \image html jkqtplotter_simpletest_datastore_statistics_2d_small.png
<td> \subpage JKQTPlotterBasicJKQTPDatastoreStatistics2D
<td> Advanced 2-Dimensional Statistical Computation with JKQTPDatastore<br>using the internal statistics library (see \ref jkqtptools_math_statistics )<br>histograms<br>kernel density estimates (KDE)
</table>

View File

@ -12,6 +12,8 @@
- \ref JKQTMathTextSimpleExample
.
\defgroup jkqtmathtext_items JKQTMathText Render-Tree Items
\ingroup jkqtmathtext
*/

View File

@ -1,32 +1,137 @@
/*!
\defgroup jkqtptools Tool Functions and Definitions for JKQTPlotter
\defgroup jkqtptools Support Function library (e.g. for JKQTPlotter)
This group contains a set of tools that I've written over the years to enhance the
C++ standard library.
This group contains several tool functions and datatypesthat are neccessary for
JKQTPlotter, JKQTMathtext and the other major classes in this library, but may
also be used separately. The functions and datatypes are sorted into several
functionaly groups.
\defgroup jkqtpplotterclasses_tools Support Classes/Structs/Functions for JKQTPlotter&JKQTBasePlotter
\defgroup jkqtptools_algorithms Diverse Algorithms
\ingroup jkqtptools
\defgroup jkqtptools_math Tools for Mathematical Computations & Equation Parsing
\defgroup jkqtptools_math Mathematical Computations & Expression Parsing
\ingroup jkqtptools
\defgroup jkqtptools_string String Tool Functions
\defgroup jkqtptools_math_basic Mathematical Functions & Tools
\ingroup jkqtptools_math
This group assembles a variety of mathematical tool functions that are used in different places.
\defgroup jkqtptools_math_array Data Array Tools
\ingroup jkqtptools_math
Functions in this group form the basis for the statistics (\ref jkqtptools_math_statistics ) and linear algebra libraries (\ref jkqtptools_math_linalg ), by providing allocation and freeing of (aligned) memory arrays.
\see JKQTPlotterBasicJKQTPDatastoreStatistics
\defgroup jkqtptools_math_linalg Linear Algebra Tools
\ingroup jkqtptools_math
This group assembles a basic set of linear algebra methods, including matrix inversion, which are required e.g. by the statistics library (\ref jkqtptools_math_statistics )
\defgroup jkqtptools_math_statistics Statistical Computations
\ingroup jkqtptools_math
This group contains a statistics library, which offers several basic methods and is based on an iterator interface:
- \ref jkqtptools_math_statistics_basic
- \ref jkqtptools_math_statistics_grouped
- \ref jkqtptools_math_statistics_regression
- \ref jkqtptools_math_statistics_poly
- \ref jkqtptools_math_statistics_1dhist
- \ref jkqtptools_math_statistics_2dhist
- \ref jkqtptools_math_statistics_1dkde
- \ref jkqtptools_math_statistics_2dkde
.
In addition there is a set of "adaptors" (see \ref jkqtptools_math_statistics_adaptors ) that shortcut the calculation of a statistical property and the subsequent parametrization of a plot with the results. With these adaptors you can add e.g. a boxplot or histogram chart to a plot by calling only one function.
All statistics functions use an iterator-based interface, comparable to the interface of the <a href="http://www.cplusplus.com/reference/algorithm/">algorithms in the C++ standard template library</a>. To this end, the class `JKQTPDatastore` provides an iterator interface to its columns, using the functions `JKQTPDatastore::begin()` and `JKQTPDatastore::end()`. Both functions simply receive the column ID as parameter and exist in a const and a mutable variant. the latter allows to also edit the data. In addition the function `JKQTPDatastore::backInserter()` returns a back-inserter iterator (like generated for STL containers with `std::back_inserter(container)`) that also allows to append to the column.
Note that the iterator interface allows to use these functions with any container that provides such iterators (e.g. `std::vector<double>`, `std::list<int>`, `std::set<float>`, `QVector<double>`...).
Code using one of these statistics functions therefore may look e.g. like this:
\code
// mean of a column in a JKQTPDatastore:
double mean=jkqtpstatAverage(datastore1->begin(randomdatacol1), datastore1->end(randomdatacol1));
// mean of a std::vector
std::vector<double> data {1,2,4,5,7,8,10,2,1,3,5};
double meanvec=jkqtpstatAverage(data.begin(), data.end());
\endcode
All statistics functions use all values in the given range and convert each value to a `double`, using `jkqtp_todouble()`. The return values is always a dohble. Therefore you can use these functions to calculate statistics of ranges of any type that can be converted to `double`. Values that do not result in a valid `double`are not used in calculating the statistics. Therefore you can exclude values by setting them `JKQTP_DOUBLE_NAN` (i.e. "not a number").
\see see for detailed examples: \ref JKQTPlotterBasicJKQTPDatastoreStatistics
\defgroup jkqtptools_math_statistics_basic Basic statistics
\ingroup jkqtptools_math_statistics
\defgroup jkqtptools_math_statistics_grouped Grouped statistics
\ingroup jkqtptools_math_statistics
\defgroup jkqtptools_math_statistics_regression Regression Analysis
\ingroup jkqtptools_math_statistics
\defgroup jkqtptools_math_statistics_poly Polynomial Fits/Regression
\ingroup jkqtptools_math_statistics
\defgroup jkqtptools_math_statistics_1dhist 1-dimensional Histograms
\ingroup jkqtptools_math_statistics
\defgroup jkqtptools_math_statistics_2dhist 2-dimensional Histograms
\ingroup jkqtptools_math_statistics
\defgroup jkqtptools_math_statistics_1dkde 1-dimensional Kernel Density Estimates
\ingroup jkqtptools_math_statistics
\defgroup jkqtptools_math_statistics_1dkde_kernels Kernels for 1-dimensional Histograms
\ingroup jkqtptools_math_statistics_1dkde
\defgroup jkqtptools_math_statistics_2dkde 2-dimensional Kernel Density Estimates
\ingroup jkqtptools_math_statistics
\defgroup jkqtptools_math_statistics_2dkde_kernels Kernels for 2-dimensional Histograms
\ingroup jkqtptools_math_statistics_2dkde
\defgroup jkqtptools_math_statistics_adaptors Statistics To Plot Adaptors
\ingroup jkqtptools_math_statistics
\defgroup jkqtptools_string String/String-Conversion Tool Functions
\ingroup jkqtptools
Offers diverse function to convert different datatypes (e.g. double, int, diverse enums) to and from strings and for string manipulation.
\defgroup jkqtptools_qt Additional Tools for Qt
\ingroup jkqtptools
\defgroup jkqtptools_qtwidgets Additional Widgets for Qt
\ingroup jkqtptools
\defgroup jkqtptools_drawing Drawing & Graphics Tools
\ingroup jkqtptools
Specialized drawing functions used by the plotters and LaTeX renderers.
\defgroup jkqtptools_debugging Debugging Tools
\ingroup jkqtptools
Functions that help during debugging (e.g. an assert function/macro).
\defgroup jkqtptools_codestructuring Code Structuring Tools
\ingroup jkqtptools
Classes and functions that help to structure the code (e.g. RAII constructs)
\defgroup jkqtptools_enums Enums for JKQTPlotter (including String Conversion)
\ingroup jkqtptools
Assembles diverse ENUMs required by JKQTPlotter.
@ -56,6 +161,9 @@ Examples:
- \ref JKQTPlotterImagePlotRGBOpenCV
.
\defgroup jkqtpplottersupprt Support Classes and Functions
\ingroup jkqtplotter
\defgroup jkqtpplotterclasses Plotter Class & Plotter Widget
\ingroup jkqtplotter
@ -126,7 +234,7 @@ With these there are usually two variants of one type of graph: One without erro
This approach allows to keep interfaces and appearance recognizeable over different graph classes and locates the source code
for a feature like error indicators in a single/in few class(es).
Another example of such a class is JKQTPColorPaletteTools, which provides functions that allow to use color palettes. It is
Another example of such a class is JKQTPColorPaletteStyleAndToolsMixin, which provides functions that allow to use color palettes. It is
mainly used for the \ref jkqtplotter_imagelots "Image/Matrix graphs", but also by e.g. JKQTPXYParametrizedScatterGraph.
@ -269,8 +377,13 @@ This group assembles graphs that show their data with symbols and optionally wit
<tr>
<td>\image html jkqtplotter_simpletest_boxplot_small.png
<td> JKQTPBoxplotVerticalGraph, JKQTPBoxplotHorizontalGraph
<tr>
<td>\image html JKQTPViolinplotVerticalElement_small.png
<td> JKQTPViolinplotVerticalElement, JKQTPViolinplotHorizontalElement
</table>
\see \ref jkqtptools_math_statistics_adaptors for shortcuts to calculate statistical properties of data and then adding a plot with the results.
\defgroup jkqtplotter_geoplots Geometric Elements (Lines, Rectangles, ...)
\ingroup jkqtplotter_graphsgroup
@ -308,6 +421,9 @@ This group assembles graphs that show their data with symbols and optionally wit
<tr>
<td>\image html geo_boxplot_small.png
<td> JKQTPBoxplotVerticalElement, JKQTPBoxplotHorizontalElement
<tr>
<td>\image html JKQTPViolinplotVerticalElement_small.png
<td> JKQTPViolinplotVerticalElement, JKQTPViolinplotHorizontalElement
</table>
Examples:
@ -353,10 +469,10 @@ Examples:
<td> JKQTPOverlayImageEnhanced, JKQTPColumnOverlayImageEnhanced
<tr>
<td>\image html JKQTPContour_small.png
<td> JKQTPContour
<td> JKQTPContourPlot, JKQTPColumnContourPlot
</table>
\defgroup jkqtplotter_imagelots_elements Matrix/Image Graphs
\defgroup jkqtplotter_imagelots_elements Image Graphs
\ingroup jkqtplotter_imagelots
@ -393,7 +509,7 @@ Examples:
\defgroup jkqtplotter_imagelots_tools Tool Functions & Classes for Image Drawing
\ingroup jkqtplotter_imagelots
\defgroup jkqtplotter_imagelots_contour Contour Graphs
\defgroup jkqtplotter_imagelots_contour Contour Graphs (based on Image Data)
\ingroup jkqtplotter_imagelots
<table>
@ -402,7 +518,7 @@ Examples:
<th> Classes
<tr>
<td>\image html JKQTPContour_small.png
<td> JKQTPContour
<td> JKQTPContourPlot, JKQTPColumnContourPlot
</table>
\defgroup jkqtplotter_overlays Overlay Elements

View File

@ -28,6 +28,8 @@
- external or internal datasets
- complete with GUI (table view)
- built-in export capabilities for datatables (e.g. to CSV, SYLK, ...)
- C++ standard iterator interface
- \ref jkqtptools_math_statistics (basic statistics, boxplots, histograms, kernel density estimates, regression analysis, polynomial fitting)
- <b>\ref jkqtplotter_elements "large variety of graphs that can be added to a plot", e.g.:</b>
- \ref jkqtplotter_linesymbolgraphs_simple "scatter-plots" (also \ref jkqtplotter_linesymbolgraphs_param "parametrized color/size/symbol by a third data-column")
- \ref jkqtplotter_linesymbolgraphs "line graphs, step graphs", \ref jkqtplotter_barssticks "impulses"

View File

@ -26,7 +26,7 @@ Changes, compared to \ref page_whatsnew_V2018_08 "v2018.08" include:
<li> changed: using static const variables instead of <code>#define</code> for fixed default values (e.g. JKQTPImageTools::LUTSIZE, JKQTPImageTools::PALETTE_ICON_WIDTH, JKQTPlotterDrawinTools::ABS_MIN_LINEWIDTH, JKQTMathText::ABS_MIN_LINEWIDTH ...)</li>
<li> Updates to JKQTPlotter:
<ul>
<li> new: added JKQTPSingleColumnSymbolsGraph for single-column data, e.g. drawn as (random) scatter or bee-swar plots </li>
<li> new: added JKQTPSingleColumnSymbolsGraph for single-column data, e.g. drawn as (random) scatter or bee-swarm or rug plots </li>
<li> new: stacked barcharts with JKQTPBarVerticalStackableGraph, JKQTPBarHorizontalStackableGraph </li>
<li> new: use/support of C++11 features (e.g. \c std::function<> and lambda functions in JKQTPXFunctionLineGraph / JKQTPYFunctionLineGraph )</li>
<li> new: \ref jkqtpopencvinterface "optional OpenCV interface" </li>
@ -44,18 +44,22 @@ Changes, compared to \ref page_whatsnew_V2018_08 "v2018.08" include:
<li> new: advanced styling options for boxplots + example for the styling: \ref JKQTPlotterBoxplotStyling </li>
<li> new: notched boxplots, see: \ref JKQTPlotterBoxplotStyling </li>
<li> new: several new plot symbols, see: JKQTPGraphSymbols </li>
<li> new: Statistics library with functions to calculate histograms, regression, kernel density estimates, ... see: \ref jkqtptools_math_statistics </li>
<li> new: iterator interface and improved documentation for JKQTPDatastore </li>
<li> new: violin plots (see \ref JKQTPlotterViolinplotGraphs ) </li>
<li> changed: removed old selection-code and replaced by general highlighting feature </li>
<li> changed: JKQTPStepHorizontalGraph has been renamed to JKQTPSpecialLineHorizontalGraph (vertical variants also) and have gained additional features (baseline for filling and drawing of symbols) </li>
<li> changed: filled curve graphs (e.g. JKQTPSpecialLineHorizontalGraph) are now merely a specializedly initialized JKQTPSpecialLineHorizontalGraph </li>
<li> removed: Removed datarange-feature </li>
<li> update: \ref JKQTPLOTTER_USERINTERACTION "reworked user interactions API" </li>
<li> update: \ref jkqtplotter_basegraphserrors "reworked error indicator base classes" </li>
<li> update: improvements to contour plots (e.g. highlighting single contour levels, split into a class for simple C-arrays [JKQTPContourPlot] and one for image data in a JKQTPDatastore column [JKQTPColumnContourPlot]) + added example, see JKQTPColumnContourPlot and \ref JKQTPlotterContourPlot </li>
<li> fixed: colorbars at top were positioned over the plot label </li>
<li> fixed: RGB-image plots now work properly with inverted axes (image is inverted, befor image was not shown at all) </li>
</ul></li>
<li> Updates to JKQTMathText:
<ul>
<li> new: slanted fractions (<code>\sfrac{}{}</code>, <code>\stfrac{}{}</code>) </li>
<li> new: slanted fractions (<code>\\sfrac{}{}</code>, <code>\\stfrac{}{}</code>) </li>
<li> update/fix: several general improvements and bugfixes </li>
<li> update/fix: improved error handling </li>
<li> update: updated contained version of STIX fonts, better handling of different STIX versions </li>

Binary file not shown.

Before

Width:  |  Height:  |  Size: 32 KiB

After

Width:  |  Height:  |  Size: 73 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 13 KiB

After

Width:  |  Height:  |  Size: 26 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 22 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.2 KiB

View File

@ -24,6 +24,7 @@ All test-projects are Qt-projects that use qmake to build. You can load them int
| [![](https://raw.githubusercontent.com/jkriege2/JKQtPlotter/master/screenshots/jkqtplotter_simpletest_parsedfunctionplot_small.png)](https://github.com/jkriege2/JKQtPlotter/tree/master/examples/simpletest_parsedfunctionplot) | [Plotting Parsed Mathematical Functions as Line Graphs](https://github.com/jkriege2/JKQtPlotter/tree/master/examples/simpletest_parsedfunctionplot) | `JKQTPXParsedFunctionLineGraph` <br> plotting functions with the internal math equation parser/evaluator |
| [![](https://raw.githubusercontent.com/jkriege2/JKQtPlotter/master/screenshots/jkqtplotter_simpletest_geometric_small.png)](https://github.com/jkriege2/JKQtPlotter/tree/master/examples/simpletest_geometric) | [Plotting Geometric Objects](https://github.com/jkriege2/JKQtPlotter/tree/master/examples/simpletest_geometric) | |
| [![](https://raw.githubusercontent.com/jkriege2/JKQtPlotter/master/screenshots/jkqtplotter_simpletest_boxplot_small.png)](https://github.com/jkriege2/JKQtPlotter/tree/master/examples/simpletest_boxplot) | [Plotting Box Plots](https://github.com/jkriege2/JKQtPlotter/tree/master/examples/simpletest_boxplot) | `JKQTPBoxplotVerticalGraph`, `JKQTPBoxplotHorizontalGraph` |
| [![](https://raw.githubusercontent.com/jkriege2/JKQtPlotter/master/screenshots/jkqtplotter_simpletest_violinplot_small.png)](https://github.com/jkriege2/JKQtPlotter/tree/master/examples/simpletest_violinplot) | [Plotting Violin Plots](https://github.com/jkriege2/JKQtPlotter/tree/master/examples/simpletest_violinplot) | `JKQTPViolinplotVerticalElement`, `JKQTPViolinplotHorizontalElement` |
## Styling the Plot, Keys, Axes, ...
@ -35,7 +36,6 @@ All test-projects are Qt-projects that use qmake to build. You can load them int
| [![](https://raw.githubusercontent.com/jkriege2/JKQtPlotter/master/screenshots/test_styledboxplot_small.png)](https://github.com/jkriege2/JKQtPlotter/tree/master/examples/test_styledboxplot) | [Styling of Boxplots](https://github.com/jkriege2/JKQtPlotter/tree/master/examples/test_styledboxplot) | Modifying different Aspects of the Styling of boxplots |
| [![](https://raw.githubusercontent.com/jkriege2/JKQtPlotter/master/screenshots/test_styling_small.png)](https://github.com/jkriege2/JKQtPlotter/tree/master/examples/test_styling) | [Styling of JKQTPlotter](https://github.com/jkriege2/JKQtPlotter/tree/master/examples/test_styling) | Modifying different Aspects of the Styling of JKQTPlotter |
## Image data Plots
| Screenshot | Description | Notes |
@ -47,6 +47,7 @@ All test-projects are Qt-projects that use qmake to build. You can load them int
| [![](https://raw.githubusercontent.com/jkriege2/JKQtPlotter/master/screenshots/jkqtplotter_simpletest_rgbimageplot_small.png)](https://github.com/jkriege2/JKQtPlotter/tree/master/examples/jkqtplotter_simpletest_rgbimageplot) | [Simple 3-channel Math RGB/CMY Image Plot](https://github.com/jkriege2/JKQtPlotter/tree/master/examples/jkqtplotter_simpletest_rgbimageplot) | `JKQTPColumnRGBMathImage` <br> image data in a C-style row-major array, not using internal datastore <br> RGB/CMY color compositing |
| [![](https://raw.githubusercontent.com/jkriege2/JKQtPlotter/master/screenshots/jkqtplotter_simpletest_imageplot_opencv_small.png)](https://github.com/jkriege2/JKQtPlotter/tree/master/examples/simpletest_imageplot_opencv) | [1-channel OpenCV cv::Mat Image Plot](https://github.com/jkriege2/JKQtPlotter/tree/master/examples/simpletest_imageplot_opencv) | `JKQTPColumnMathImage` <br> image data copied from OpenCV cv::Mat-structure into a single column of the internal datastore |
| [![](https://raw.githubusercontent.com/jkriege2/JKQtPlotter/master/screenshots/jkqtplotter_simpletest_rgbimageplot_opencv_small.png)](https://github.com/jkriege2/JKQtPlotter/tree/master/examples/simpletest_rgbimageplot_opencv) | [RGB OpenCV cv::Mat Image Plot](https://github.com/jkriege2/JKQtPlotter/tree/master/examples/simpletest_rgbimageplot_opencv) | `JKQTPColumnRGBMathImage` <br> image data copied from OpenCV cv::Mat-structure into three columns <br> inverted coordinate axesof the internal datastore |
| [![](https://raw.githubusercontent.com/jkriege2/JKQtPlotter/master/screenshots/jkqtplotter_simpletest_contourplot_small.png)](https://github.com/jkriege2/JKQtPlotter/tree/master/examples/simpletest_contourplot) | [Contour Plots](https://github.com/jkriege2/JKQtPlotter/tree/master/examples/simpletest_contourplot) | `JKQTPColumnContourPlot` <br> image data edited inside JKQTPDatastore |
@ -58,23 +59,16 @@ All test-projects are Qt-projects that use qmake to build. You can load them int
| [![](https://raw.githubusercontent.com/jkriege2/JKQtPlotter/master/screenshots/jkqtplotter_test_user_interaction_small.gif)](https://github.com/jkriege2/JKQtPlotter/tree/master/examples/test_user_interaction) | [User Interaction](https://github.com/jkriege2/JKQtPlotter/tree/master/examples/test_user_interaction) | different possibilities of user-interaction in JKQtPlotter |
## Data Management
## Data Management & Statistics (Tutorials)
| Screenshot | Description | Notes |
|:-------------:| ------------- | ------------- |
| [![](https://raw.githubusercontent.com/jkriege2/JKQtPlotter/master/screenshots/simpletest_datastore_small.png)](https://github.com/jkriege2/JKQtPlotter/tree/master/examples/simpletest_datastore) | [Tutorial: Basic Usage of JKQTPDatastore](https://github.com/jkriege2/JKQtPlotter/tree/master/examples/simpletest_datastore) | Basic Data Management with JKQTPDatastore <br/> Copying data into a JKQTPDatastore <br/> Editing data inside a JKQTPDatastore <br/> Editing Image Data in a JKQTPDatastore |
## More Complex Examples
| Screenshot | Description | Notes |
|:-------------:| ------------- | ------------- |
| [![](https://raw.githubusercontent.com/jkriege2/JKQtPlotter/master/screenshots/test_multiplot_small.png)](https://github.com/jkriege2/JKQtPlotter/tree/master/examples/test_multiplot) | [Layouting Several Plots](https://github.com/jkriege2/JKQtPlotter/tree/master/examples/test_multiplot) | Combining plots in Qt Layouts <br> linking plot axes <br> copy data from a `std::map` int the datastore <br> print plots/print preview |
| [![](https://raw.githubusercontent.com/jkriege2/JKQtPlotter/master/screenshots/test_distributionplot_small.png)](https://github.com/jkriege2/JKQtPlotter/tree/master/examples/test_distributionplot) | [Plotting a Statistical Distribution of Data](https://github.com/jkriege2/JKQtPlotter/tree/master/examples/test_distributionplot) | Combines several different graphs to draw random values, their distribution and some statistical properties |
| [![](https://raw.githubusercontent.com/jkriege2/JKQtPlotter/master/screenshots/simpletest_datastore_iterators_small.png)](https://github.com/jkriege2/JKQtPlotter/tree/master/examples/simpletest_datastore_iterators) | [Tutorial: Iterator-based access to JKQTPDatastore](https://github.com/jkriege2/JKQtPlotter/tree/master/examples/simpletest_datastore_iterators) | Iterator-based Data Management with JKQTPDatastore |
| [![](https://raw.githubusercontent.com/jkriege2/JKQtPlotter/master/screenshots/jkqtplotter_simpletest_datastore_statistics_small.png)](https://github.com/jkriege2/JKQtPlotter/tree/master/examples/simpletest_datastore_statistics) | [Tutorial: Advanced 1-Dimensional Statistics with JKQTPDatastore](https://github.com/jkriege2/JKQtPlotter/tree/master/examples/simpletest_datastore_statistics) | Advanced 1-Dimensional Statistical Computation with JKQTPDatastore<br>using the internal statistics library<br>basic statistics (mean, standard deviation, ...)<br>boxplots<br>histograms<br>kernel density estimates (KDE) |
| [![](https://raw.githubusercontent.com/jkriege2/JKQtPlotter/master/screenshots/jkqtplotter_simpletest_datastore_regression_small.png)](https://github.com/jkriege2/JKQtPlotter/tree/master/examples/simpletest_datastore_regression) | [Tutorial: Regression Analysis (with the Statistics Library)](https://github.com/jkriege2/JKQtPlotter/tree/master/examples/simpletest_datastore_regression) | Advanced 1-Dimensional Statistical Computation with JKQTPDatastore<br>using the internal statistics library<br>Regression Analysis (with the Statistics Library)<br>robust regression (IRLS)<br>weighted regression<br>non-linear regression<br>polynomial fitting |
| [![](https://raw.githubusercontent.com/jkriege2/JKQtPlotter/master/screenshots/jkqtplotter_simpletest_datastore_groupedstat_small.png)](https://github.com/jkriege2/JKQtPlotter/tree/master/examples/simpletest_datastore_groupedstat) | [Tutorial: 1-Dimensional Group Statistics (with the Statistics Library)](https://github.com/jkriege2/JKQtPlotter/tree/master/examples/simpletest_datastore_groupedstat) | Advanced 1-Dimensional Statistical Computation with JKQTPDatastore<br>grouped statistics<br>error indicators from data<br>boxplots |
| [![](https://raw.githubusercontent.com/jkriege2/JKQtPlotter/master/screenshots/jkqtplotter_simpletest_datastore_statistics_2d_small.png)](https://github.com/jkriege2/JKQtPlotter/tree/master/examples/simpletest_datastore_statistics_2d) | [Tutorial: Advanced 2-Dimensional Statistics with JKQTPDatastore](https://github.com/jkriege2/JKQtPlotter/tree/master/examples/simpletest_datastore_statistics_2d) | Advanced 2-Dimensional Statistical Computation with JKQTPDatastore<br>using the internal statistics library<br>histograms<br>kernel density estimates (KDE) |
## More Complex Examples

View File

@ -37,3 +37,5 @@ CONFIG (debug, debug|release) {
LIBS += -L../../staticlib/jkqtfastplotterlib/release -ljkqtfastplotterlib
}
message("LIBS = $$LIBS")
win32-msvc*: DEFINES += _USE_MATH_DEFINES
win32-msvc*: DEFINES += NOMINMAX

View File

@ -16,3 +16,6 @@ CONFIG (debug, debug|release) {
LIBS += -L../../staticlib/jkqtplotterlib/release -ljkqtplotterlib
}
message("LIBS = $$LIBS")
win32-msvc*: DEFINES += _USE_MATH_DEFINES
win32-msvc*: DEFINES += NOMINMAX

View File

@ -39,13 +39,12 @@ DEPENDPATH += ../../lib ../../staticlib/jkqtmathtextlib
INCLUDEPATH += ../../lib
CONFIG (debug, debug|release) {
DEPENDPATH += ../../staticlib/jkqtmathtextlib/debug
DEPENDPATH += ../../staticlib/jkqtphighrestimerlib/debug
LIBS += -L../../staticlib/jkqtmathtextlib/debug -ljkqtmathtextlib_debug
LIBS += -L../../staticlib/jkqtphighrestimerlib/debug -ljkqtphighrestimerlib_debug
} else {
DEPENDPATH += ../../staticlib/jkqtmathtextlib/release
DEPENDPATH += ../../staticlib/jkqtphighrestimerlib/release
LIBS += -L../../staticlib/jkqtmathtextlib/release -ljkqtmathtextlib
LIBS += -L../../staticlib/jkqtphighrestimerlib/release -ljkqtphighrestimerlib
}
message("LIBS = $$LIBS")
win32-msvc*: DEFINES += _USE_MATH_DEFINES
win32-msvc*: DEFINES += NOMINMAX

View File

@ -1,9 +1,8 @@
TEMPLATE = subdirs
SUBDIRS += jkqtmathtextlib jkqtphighrestimerlib jkqtmathtext_test
SUBDIRS += jkqtmathtextlib jkqtmathtext_test
jkqtmathtextlib.file = ../../staticlib/jkqtmathtextlib/jkqtmathtextlib.pro
jkqtphighrestimerlib.file = ../../staticlib/jkqtphighrestimerlib/jkqtphighrestimerlib.pro
jkqtmathtext_test.file=$$PWD/jkqtmathtext_test.pro
jkqtmathtext_test.depends = jkqtmathtextlib jkqtphighrestimerlib
jkqtmathtext_test.depends = jkqtmathtextlib

View File

@ -4,7 +4,7 @@
#include <QWidget>
#include <QStringList>
#include "jkqtmathtext/jkqtmathtext.h"
#include "jkqtplottertools/jkqtphighrestimer.h"
#include "jkqtcommon/jkqtphighrestimer.h"
#include <QPainter>
#include <QTreeWidget>

View File

@ -17,7 +17,7 @@
#include "jkqtplotter/jkqtpgraphsparsedfunction.h"
#include "jkqtplotter/jkqtpbaseelements.h"
#include "jkqtplottergui/jkqtpgraphsmodel.h"
#include "jkqtcommon/jkqtptools.h"
#include "jkqtplotter/jkqtptools.h"
#define N1 200
#define N2 50

View File

@ -90,10 +90,10 @@ TestWidgetContourPlots::TestWidgetContourPlots(QWidget *parent) :
plotContour->setGrid(false);
plotDensity->zoom(-0.1,1.1,-0.1,1.1);
QList<double> levels;
QVector<double> levels;
// levels<<5<<10<<25; // levels<<5.1<<10.1;
levels<<4<<5<<9<<14;
JKQTPContour* cp=new JKQTPContour(plotContour->getPlotter());
JKQTPColumnContourPlot* cp=new JKQTPColumnContourPlot(plotContour->getPlotter());
cp->setX(-0.1);
cp->setY(-0.1);
cp->setWidth(1.1);

View File

@ -18,7 +18,7 @@
#include "jkqtplotter/jkqtpgraphsparsedfunction.h"
#include "jkqtplotter/jkqtpbaseelements.h"
#include "jkqtplottergui/jkqtpgraphsmodel.h"
#include "jkqtcommon/jkqtptools.h"
#include "jkqtplotter/jkqtptools.h"
#define N1 200
#define N2 50

View File

@ -17,7 +17,7 @@
#include "jkqtplotter/jkqtpgraphsparsedfunction.h"
#include "jkqtplotter/jkqtpbaseelements.h"
#include "jkqtplottergui/jkqtpgraphsmodel.h"
#include "jkqtcommon/jkqtptools.h"
#include "jkqtplotter/jkqtptools.h"
#define N1 200
#define N2 50

View File

@ -18,7 +18,7 @@
#include "jkqtplotter/jkqtpbaseelements.h"
#include "jkqtplottergui/jkqtpgraphsmodel.h"
#include "jkqtplottergui/jkqtpcomboboxes.h"
#include "jkqtcommon/jkqtptools.h"
#include "jkqtplotter/jkqtptools.h"
#define N1 200
#define N2 50

View File

@ -17,7 +17,7 @@
#include "jkqtplotter/jkqtpgraphsparsedfunction.h"
#include "jkqtplotter/jkqtpbaseelements.h"
#include "jkqtplottergui/jkqtpgraphsmodel.h"
#include "jkqtcommon/jkqtptools.h"
#include "jkqtplotter/jkqtptools.h"
#define N1 200
#define N2 50

View File

@ -17,7 +17,7 @@
#include "jkqtplotter/jkqtpgraphsparsedfunction.h"
#include "jkqtplotter/jkqtpbaseelements.h"
#include "jkqtplottergui/jkqtpgraphsmodel.h"
#include "jkqtcommon/jkqtptools.h"
#include "jkqtplotter/jkqtptools.h"
#define N1 200
#define N2 50

View File

@ -17,7 +17,7 @@
#include "jkqtplotter/jkqtpgraphsparsedfunction.h"
#include "jkqtplotter/jkqtpbaseelements.h"
#include "jkqtplottergui/jkqtpgraphsmodel.h"
#include "jkqtcommon/jkqtptools.h"
#include "jkqtplotter/jkqtptools.h"
#define N1 200
#define N2 50

View File

@ -17,7 +17,7 @@
#include "jkqtplotter/jkqtpgraphsparsedfunction.h"
#include "jkqtplotter/jkqtpbaseelements.h"
#include "jkqtplottergui/jkqtpgraphsmodel.h"
#include "jkqtcommon/jkqtptools.h"
#include "jkqtplotter/jkqtptools.h"

View File

@ -17,7 +17,7 @@
#include "jkqtplotter/jkqtpgraphsparsedfunction.h"
#include "jkqtplotter/jkqtpbaseelements.h"
#include "jkqtplottergui/jkqtpgraphsmodel.h"
#include "jkqtcommon/jkqtptools.h"
#include "jkqtplotter/jkqtptools.h"
#define N1 200
#define N2 50

View File

@ -18,7 +18,7 @@
#include "jkqtplotter/jkqtpgraphsparsedfunction.h"
#include "jkqtplotter/jkqtpbaseelements.h"
#include "jkqtplottergui/jkqtpgraphsmodel.h"
#include "jkqtcommon/jkqtptools.h"
#include "jkqtplotter/jkqtptools.h"
#define N1 200
#define N2 50

View File

@ -49,6 +49,7 @@ RESOURCES += jkqtplot_test.qrc
win32-msvc*: DEFINES += _USE_MATH_DEFINES
win32-msvc*: DEFINES += NOMINMAX
TARGET = jkqtplot_test

View File

@ -20,6 +20,7 @@ CONFIG (debug, debug|release) {
message("LIBS = $$LIBS")
win32-msvc*: DEFINES += _USE_MATH_DEFINES
win32-msvc*: DEFINES += NOMINMAX

View File

@ -29,6 +29,8 @@ win32-msvc* {
# To fix error: C2338: va_start argument must not
# have reference type and must not be parenthesized
DEFINES += _CRT_NO_VA_START_VALIDATION
# fix errors with min()/max() macros from windows
DEFINES += NOMINMAX
}

View File

@ -20,6 +20,8 @@ CONFIG (debug, debug|release) {
message("LIBS = $$LIBS")
win32-msvc*: DEFINES += _USE_MATH_DEFINES
win32-msvc*: DEFINES += NOMINMAX

View File

@ -1,6 +1,13 @@
# Example (JKQTPlotter): Boxplots {#JKQTPlotterBoxplotsGraphs}
This project (see [`simpletest_boxplot`](https://github.com/jkriege2/JKQtPlotter/tree/master/examples/simpletest_boxplot) demonstrates how to use JKQTPlotter to draw <a href="https://en.wikipedia.org/wiki/Box_plot">box plots</a> using the classes `JKQTPBoxplotVerticalGraph` and `JKQTPBoxplotHorizontalGraph`.
[JKQTPlotterBasicJKQTPDatastoreStatistics]: @ref JKQTPlotterBasicJKQTPDatastoreStatistics "Advanced 1-Dimensional Statistics with JKQTPDatastore"
[statisticslibrary]: @ref jkqtptools_math_statistics "JKQTPlotter Statistics Library"
[JKQTPlotterBoxplotStyling]: @ref JKQTPlotterBoxplotStyling "Styling different aspects of boxplots"
***Note*** that this example explains how to add boxplots to a graph by hand, i.e. by calculating all the statistical properties for the boxplots by hand. The internal [statisticslibrary] offers methods to perform these calculations, which are explained in the tutorial [JKQTPlotterBasicJKQTPDatastoreStatistics] in detail. Additional advanced styling of boxplots is covered by the example [JKQTPlotterBoxplotStyling].
The source code of the main application is (see [`jkqtplotter_simpletest_boxplot.cpp`](jkqtplotter_simpletest_boxplot.cpp).
After adding all necessary data to the JKQTDatastore:

View File

@ -21,5 +21,7 @@ message("LIBS = $$LIBS")
win32-msvc*: DEFINES += _USE_MATH_DEFINES
win32-msvc*: DEFINES += NOMINMAX

View File

@ -0,0 +1,131 @@
# Example (JKQTPlotter): Contour Plots {#JKQTPlotterContourPlot}
This project (see `./examples/simpletest_contourplot/`) shows how to draw contour plots with JKQTPlotter.
[TOC]
# Drawing a Contour Plot
The source code of the main application is (see [`jkqtplotter_simpletest_contourplot.cpp`](https://github.com/jkriege2/JKQtPlotter/tree/master/examples/simpletest_contourplot/jkqtplotter_simpletest_contourplot.cpp) ).
First the electric potential from a quadrupole is calculated and stored in an image column inside the JKQTPDatastore:
```.cpp
JKQTPDatastore* ds=plot.getDatastore();
const int NX=500; // image dimension in x-direction [pixels]
const int NY=500; // image dimension in y-direction [pixels]
const double w=2.7e-6;
const double dx=w/static_cast<double>(NX);
const double h=NY*dx;
size_t cPotential=ds->addImageColumn(NX, NY, "imagedata");
double x;
double y=-h/2.0;
const double eps0=8.854187e-12;
const double Q1=1.6e-19; // charge of charged particle 1
const double Q1_x0=-0.5e-6; // x-position of charged particle 1
const double Q1_y0=-0.5e-6; // y-position of charged particle 1
const double Q2=1.6e-19; // charge of charged particle 2
const double Q2_x0=0.5e-6; // x-position of charged particle 2
const double Q2_y0=0.5e-6; // y-position of charged particle 2
const double Q3=-1.6e-19; // charge of charged particle 3
const double Q3_x0=-0.5e-6; // x-position of charged particle 3
const double Q3_y0=0.5e-6; // y-position of charged particle 3
const double Q4=-1.6e-19; // charge of charged particle 4
const double Q4_x0=0.5e-6; // x-position of charged particle 4
const double Q4_y0=-0.5e-6; // y-position of charged particle 4
for (size_t iy=0; iy<NY; iy++ ) {
x=-w/2.0;
for (size_t ix=0; ix<NX; ix++ ) {
const double r1=sqrt((x-Q1_x0)*(x-Q1_x0)+(y-Q1_y0)*(y-Q1_y0));
const double r2=sqrt((x-Q2_x0)*(x-Q2_x0)+(y-Q2_y0)*(y-Q2_y0));
const double r3=sqrt((x-Q3_x0)*(x-Q3_x0)+(y-Q3_y0)*(y-Q3_y0));
const double r4=sqrt((x-Q4_x0)*(x-Q4_x0)+(y-Q4_y0)*(y-Q4_y0));
ds->setPixel(cPotential, ix, iy, Q1/(4.0*M_PI*eps0)/r1+Q2/(4.0*M_PI*eps0)/r2+Q3/(4.0*M_PI*eps0)/r3+Q4/(4.0*M_PI*eps0)/r4);
x+=dx;
}
y+=dx;
}
```
Then this image column can be drawn with a `JKQTPColumnContourPlot`:
```.cpp
JKQTPColumnContourPlot* graph=new JKQTPColumnContourPlot(&plot);
graph->setTitle("");
// image column with the data
graph->setImageColumn(cPotential);
// where does the image start in the plot, given in plot-axis-coordinates (bottom-left corner)
graph->setX(-w/2.0);
graph->setY(-h/2.0);
// width and height of the image in plot-axis-coordinates
graph->setWidth(w);
graph->setHeight(h);
// color-map is "BlueGreenRed"
graph->setPalette(JKQTPMathImageBlueGreenRed);
// get coordinate axis of color-bar and set its label
graph->getColorBarRightAxis()->setAxisLabel("electric potential [V]");
// add some levels for the contours. These are chosen to be at the actual potential values
// at several specified relative distance from Q1, i.e. at phi(Q1_x0*reldist) (phi: potential of Q1)
QVector<double> reldists; reldists<<0.1<<0.25<<0.5<<1<<1.5<<2<<2.5<<3;
// finally contour levels with +1 and -1 sign are added to show the positive and negative potential:
for (auto reldist: reldists) {
const double level=fabs(Q1/(4.0*M_PI*eps0)/(Q1_x0*reldist));
graph->addContourLevel(-level);
graph->addContourLevel(level);
}
qDebug()<<graph->getContourLevels();
graph->setAutoImageRange(false);
graph->setImageMin(graph->getContourLevels().first());
graph->setImageMax(graph->getContourLevels().last());
```
Note that we created the list of contour levels to draw explicitly here using `JKQTPColumnContourPlot::addContourLevel()`. There are also methods `JKQTPColumnContourPlot::createContourLevels()` and `JKQTPColumnContourPlot::createContourLevelsLog()` to auto-generate these from the data-range with linear or logarithmic spacing, but both options do not yield good results here. The code above generates these contour levels:
```
-0.0287602, -0.0115041, -0.00575203, -0.00287602, -0.00191734, -0.00143801, -0.00115041, -0.000958672, 0.000958672, 0.00115041, 0.00143801, 0.00191734, 0.00287602, 0.00575203, 0.0115041, 0.0287602
```
The result looks like this:
![jkqtplotter_simpletest_contourplot](https://raw.githubusercontent.com/jkriege2/JKQtPlotter/master/screenshots/jkqtplotter_simpletest_contourplot.png)
# Styling a Contour Plot
You can change the way that the colors for the contours are chosen by calling `JKQTPColumnContourPlot::setContourColoringMode()` with another mode:
- `JKQTPColumnContourPlot::SingleColorContours` uses the same color (set by `JKQTPColumnContourPlot::setLineColor()`) for all contours.<br>
![jkqtplotter_simpletest_contourplot](https://raw.githubusercontent.com/jkriege2/JKQtPlotter/master/screenshots/jkqtplotter_simpletest_contourplot_SingleColorContours.png)
- `JKQTPColumnContourPlot::ColorContoursFromPaletteByValue` is the mode used for the example above, which chooses the color from the current color-palette based on the current image data range and the actual level of the contour line. <br>
![jkqtplotter_simpletest_contourplot](https://raw.githubusercontent.com/jkriege2/JKQtPlotter/master/screenshots/jkqtplotter_simpletest_contourplot_ColorContoursFromPaletteByValue.png)
- `JKQTPColumnContourPlot::ColorContoursFromPalette` chooses the color by evenly spacing the contour lines over the full color palette. the line-color will then have no connection to the actual value of the level.<br>
![jkqtplotter_simpletest_contourplot](https://raw.githubusercontent.com/jkriege2/JKQtPlotter/master/screenshots/jkqtplotter_simpletest_contourplot_ColorContoursFromPalette.png)
In all modes you can override the coloring of single levels by calling `JKQTPColumnContourPlot::setOverrideColor(level, color)`. In the example above this looks like this:
```.cpp
for (auto reldist: reldists) {
const double level=fabs(Q1/(4.0*M_PI*eps0)/(Q1_x0*reldist));
graph->addContourLevel(-level);
graph->addContourLevel(level);
// set a special color for some lines:
if (reldist==1) {
graph->setOverrideColor(-level, QColor("yellow"));
graph->setOverrideColor(level, QColor("yellow"));
}
}
```
This code results (in the default coloring mode `JKQTPColumnContourPlot::ColorContoursFromPaletteByValue`) in:
![jkqtplotter_simpletest_contourplot](https://raw.githubusercontent.com/jkriege2/JKQtPlotter/master/screenshots/jkqtplotter_simpletest_contourplot_overridecolors.png)
# Gimmick: Animating a Contour Plot
In order to demonstrate the caching implemented in the contour plot, there is optional animation code inside this example, in the form of the class `ContourPlotAnimator` (see (see [`ContourPlotAnimator.cpp`](https://github.com/jkriege2/JKQtPlotter/tree/master/examples/simpletest_contourplot/ContourPlotAnimator.cpp) ).
The code therein results in an animation like this:
![jkqtplotter_simpletest_contourplot](https://raw.githubusercontent.com/jkriege2/JKQtPlotter/master/screenshots/jkqtplotter_simpletest_contourplot_animated.gif)
Note that zooming can still be perfomred without the need to recalculate the contour lines.

View File

@ -0,0 +1,51 @@
#include "contourplotanimator.h"
ContourPlotAnimator::ContourPlotAnimator(JKQTPDatastore *ds_, JKQTPlotter *pplot_, int NX_, int NY_, double w_, double h_, double dx_, size_t cPotential_):
QObject(pplot_), angle(0), ds(ds_), pplot(pplot_), NX(NX_), NY(NY_), w(w_), h(h_), dx(dx_), cPotential(cPotential_)
{
}
void ContourPlotAnimator::start(int delayMS) {
timerFR.start();
QTimer::singleShot(delayMS, this,&ContourPlotAnimator::step);
}
void ContourPlotAnimator::step() {
double x;
double y=-h/2.0;
const double eps0=8.854187e-12;
double r=1e-6*(5.0+cos(angle/4.0))/6.0;
double r2=1e-6*(5.0+sin(angle/4.0))/6.0;
const double Q1=1.6e-19; // charge of charged particle 1
const double Q1_x0=-r*cos(angle); // x-position of charged particle 1
const double Q1_y0=-r*sin(angle); // y-position of charged particle 1
const double Q2=1.6e-19; // charge of charged particle 2
const double Q2_x0=r*cos(angle); // x-position of charged particle 2
const double Q2_y0=r*sin(angle); // y-position of charged particle 2
const double Q3=-1.6e-19; // charge of charged particle 3
const double Q3_x0=-r2*cos(angle); // x-position of charged particle 3
const double Q3_y0=r2*sin(angle); // y-position of charged particle 3
const double Q4=-1.6e-19; // charge of charged particle 4
const double Q4_x0=r2*cos(angle); // x-position of charged particle 4
const double Q4_y0=-r2*sin(angle); // y-position of charged particle 4
for (size_t iy=0; iy<NY; iy++ ) {
x=-w/2.0;
for (size_t ix=0; ix<NX; ix++ ) {
const double r1=sqrt((x-Q1_x0)*(x-Q1_x0)+(y-Q1_y0)*(y-Q1_y0));
const double r2=sqrt((x-Q2_x0)*(x-Q2_x0)+(y-Q2_y0)*(y-Q2_y0));
const double r3=sqrt((x-Q3_x0)*(x-Q3_x0)+(y-Q3_y0)*(y-Q3_y0));
const double r4=sqrt((x-Q4_x0)*(x-Q4_x0)+(y-Q4_y0)*(y-Q4_y0));
ds->setPixel(cPotential, ix, iy, Q1/(4.0*M_PI*eps0)/r1+Q2/(4.0*M_PI*eps0)/r2+Q3/(4.0*M_PI*eps0)/r3+Q4/(4.0*M_PI*eps0)/r4);
x+=dx;
}
y+=dx;
}
pplot->redrawPlot();
qDebug()<<"animation step: angle="<<angle<<", framerate="<<1.0/(timerFR.elapsed()/1000.0)<<"fps <==> "<<timerFR.elapsed()<<"ms/frame";
pplot->setWindowTitle("JKQTPColumnContourPlot ["+QString::number(1.0/(timerFR.elapsed()/1000.0))+"fps]");
angle+=(2.0/180.0*M_PI);
timerFR.start();
QTimer::singleShot(1, this,SLOT(step()));
}

View File

@ -0,0 +1,35 @@
#ifndef CONTOURPLOTANIMATOR_H
#define CONTOURPLOTANIMATOR_H
#include <QObject>
#include <QTimer>
#include <QElapsedTimer>
#include <cmath>
#include "jkqtplotter/jkqtplotter.h"
#ifndef M_PI
#define M_PI 3.14159265358979323846
#endif
class ContourPlotAnimator: public QObject {
Q_OBJECT
public:
ContourPlotAnimator(JKQTPDatastore* ds_, JKQTPlotter* pplot_, int NX_, int NY_,double w_, double h_, double dx_, size_t cPotential_);
void start(int delayMS);
protected slots:
void step();
protected:
double angle;
JKQTPDatastore* ds;
JKQTPlotter* pplot;
int NX;
int NY;
double w;
double h;
double dx;
size_t cPotential;
QElapsedTimer timerFR;
};
#endif // CONTOURPLOTANIMATOR_H

View File

@ -0,0 +1,155 @@
/** \example jkqtplotter_simpletest_contourplot.cpp
* Shows how to plot a contour plot with JKQTPlotter
*
* \ref JKQTPlotterContourPlot
*/
#include <QApplication>
#include <cmath>
#include "jkqtplotter/jkqtplotter.h"
#include "jkqtplotter/jkqtpgraphscontour.h"
#ifndef M_PI
#define M_PI 3.14159265358979323846
#endif
// if defined, an animation is shown
//#define ANIMATE
#ifdef ANIMATE
#include "contourplotanimator.h"
#endif
int main(int argc, char* argv[])
{
QApplication app(argc, argv);
// 1. create a plotter window and get a pointer to the internal datastore (for convenience)
JKQTPlotter plot;
plot.getPlotter()->setUseAntiAliasingForGraphs(true); // nicer (but slower) plotting
plot.getPlotter()->setUseAntiAliasingForSystem(true); // nicer (but slower) plotting
plot.getPlotter()->setUseAntiAliasingForText(true); // nicer (but slower) text rendering
// 2. calculate image of the electric field of a quadrupolpol
JKQTPDatastore* ds=plot.getDatastore();
#ifdef ANIMATE
const int NX=200; // image dimension in x-direction [pixels]
const int NY=200; // image dimension in x-direction [pixels]
#else
const int NX=500; // image dimension in x-direction [pixels]
const int NY=500; // image dimension in x-direction [pixels]
#endif
const double w=2.7e-6;
const double dx=w/static_cast<double>(NX);
const double h=NY*dx;
size_t cPotential=ds->addImageColumn(NX, NY, "imagedata");
double x;
double y=-h/2.0;
const double eps0=8.854187e-12;
const double Q1=1.6e-19; // charge of charged particle 1
const double Q1_x0=-0.5e-6; // x-position of charged particle 1
const double Q1_y0=-0.5e-6; // y-position of charged particle 1
const double Q2=1.6e-19; // charge of charged particle 2
const double Q2_x0=0.5e-6; // x-position of charged particle 2
const double Q2_y0=0.5e-6; // y-position of charged particle 2
const double Q3=-1.6e-19; // charge of charged particle 3
const double Q3_x0=-0.5e-6; // x-position of charged particle 3
const double Q3_y0=0.5e-6; // y-position of charged particle 3
const double Q4=-1.6e-19; // charge of charged particle 4
const double Q4_x0=0.5e-6; // x-position of charged particle 4
const double Q4_y0=-0.5e-6; // y-position of charged particle 4
for (size_t iy=0; iy<NY; iy++ ) {
x=-w/2.0;
for (size_t ix=0; ix<NX; ix++ ) {
const double r1=sqrt((x-Q1_x0)*(x-Q1_x0)+(y-Q1_y0)*(y-Q1_y0));
const double r2=sqrt((x-Q2_x0)*(x-Q2_x0)+(y-Q2_y0)*(y-Q2_y0));
const double r3=sqrt((x-Q3_x0)*(x-Q3_x0)+(y-Q3_y0)*(y-Q3_y0));
const double r4=sqrt((x-Q4_x0)*(x-Q4_x0)+(y-Q4_y0)*(y-Q4_y0));
ds->setPixel(cPotential, ix, iy, Q1/(4.0*M_PI*eps0)/r1+Q2/(4.0*M_PI*eps0)/r2+Q3/(4.0*M_PI*eps0)/r3+Q4/(4.0*M_PI*eps0)/r4);
x+=dx;
}
y+=dx;
}
// the following code will plot a dipole potential instead of the quadrupole after 5s
// this tests the recognition of altered data
#ifdef ANIMATE
JKQTPlotter* pplot=&plot;
ContourPlotAnimator animation(ds,pplot,NX,NY,w,h,dx,cPotential);
animation.start(3000);
#endif
// 3. create a graph (JKQTPColumnContourPlot) with the column created above as data
JKQTPColumnContourPlot* graph=new JKQTPColumnContourPlot(&plot);
graph->setTitle("");
// image column with the data
graph->setImageColumn(cPotential);
// where does the image start in the plot, given in plot-axis-coordinates (bottom-left corner)
graph->setX(-w/2.0);
graph->setY(-h/2.0);
// width and height of the image in plot-axis-coordinates
graph->setWidth(w);
graph->setHeight(h);
// color-map is "BlueGreenRed"
graph->setPalette(JKQTPMathImageBlueGreenRed);
// get coordinate axis of color-bar and set its label
graph->getColorBarRightAxis()->setAxisLabel("electric potential [V]");
// add some levels for the contours. These are chosen to be at the actual potential values
// at several specified relative distance from Q1, i.e. at phi(Q1_x0*reldist) (phi: potential of Q1)
QVector<double> reldists; reldists<<0.1<<0.25<<0.5<<1<<1.5<<2<<2.5<<3;
// finally contour levels with +1 and -1 sign are added to show the positive and negative potential:
for (auto reldist: reldists) {
const double level=fabs(Q1/(4.0*M_PI*eps0)/(Q1_x0*reldist));
graph->addContourLevel(-level);
graph->addContourLevel(level);
// set a special color for some lines:
//if (reldist==1) {
// graph->setOverrideColor(-level, QColor("yellow"));
// graph->setOverrideColor(level, QColor("yellow"));
//}
}
qDebug()<<graph->getContourLevels();
graph->setAutoImageRange(false);
graph->setImageMin(graph->getContourLevels().first());
graph->setImageMax(graph->getContourLevels().last());
// all contour lines have the same color:
//graph->setContourColoringMode(JKQTPColumnContourPlot::SingleColorContours);
//graph->setLineColor(QColor("magenta"));
// color contour lines from palette, but wothout taking their actual level value into account:
//graph->setContourColoringMode(JKQTPColumnContourPlot::ColorContoursFromPalette);
// 4. add the graphs to the plot, so it is actually displayed
plot.addGraph(graph);
// 5. set axis labels
plot.getXAxis()->setAxisLabel("x [m]");
plot.getYAxis()->setAxisLabel("y [m]");
// 6. fix axis and plot aspect ratio to 1
plot.getPlotter()->setMaintainAspectRatio(true);
plot.getPlotter()->setAspectRatio(w/h);
plot.getPlotter()->setMaintainAxisAspectRatio(true);
plot.getPlotter()->setAxisAspectRatio(w/h);
// 7 autoscale the plot so the graph is contained
plot.zoomToFit();
// 8. show plotter and make it a decent size
plot.show();
plot.resize(800,800);
plot.setWindowTitle("JKQTPColumnContourPlot");
return app.exec();
}

View File

@ -0,0 +1,31 @@
# source code for this simple demo
SOURCES = jkqtplotter_simpletest_contourplot.cpp \
contourplotanimator.cpp
# configure Qt
CONFIG += link_prl qt
QT += core gui xml svg
greaterThan(QT_MAJOR_VERSION, 4): QT += widgets printsupport
# output executable name
TARGET = jkqtplotter_simpletest_contourplot
# include JKQTPlotter source code
DEPENDPATH += ../../lib ../../staticlib/jkqtplotterlib
INCLUDEPATH += ../../lib
CONFIG (debug, debug|release) {
LIBS += -L../../staticlib/jkqtplotterlib/debug -ljkqtplotterlib_debug
} else {
LIBS += -L../../staticlib/jkqtplotterlib/release -ljkqtplotterlib
}
message("LIBS = $$LIBS")
HEADERS += \
contourplotanimator.h
win32-msvc*: DEFINES += _USE_MATH_DEFINES
win32-msvc*: DEFINES += NOMINMAX

View File

@ -0,0 +1,8 @@
TEMPLATE = subdirs
SUBDIRS += jkqtplotterlib jkqtplotter_simpletest_contourplot
jkqtplotterlib.file = ../../staticlib/jkqtplotterlib/jkqtplotterlib.pro
jkqtplotter_simpletest_contourplot.file=$$PWD/jkqtplotter_simpletest_contourplot.pro
jkqtplotter_simpletest_contourplot.depends = jkqtplotterlib

View File

@ -1,7 +1,25 @@
# Tutorial (JKQTPlotter): Basic Usage of JKQTPDatastore {#JKQTPlotterBasicJKQTPDatastore}
# Tutorial (JKQTPDatastore): Basic Usage of JKQTPDatastore {#JKQTPlotterBasicJKQTPDatastore}
This project (see `./examples/simpletest_datastore/`) explains several advanced options of JKQTPDatastore, which is the class used to centrally store the data for (most) graphs on a JKQTPlotter widget.
[JKQTPlotterBasicJKQTPDatastore]: @ref JKQTPlotterBasicJKQTPDatastore "Basic Usage of JKQTPDatastore"
[JKQTPlotterBasicJKQTPDatastoreIterators]: @ref JKQTPlotterBasicJKQTPDatastoreIterators "Iterator-Based usage of JKQTPDatastore"
[JKQTPlotterBasicJKQTPDatastoreStatistics]: @ref JKQTPlotterBasicJKQTPDatastoreStatistics "Advanced 1-Dimensional Statistics with JKQTPDatastore"
[JKQTPlotterBasicJKQTPDatastoreRegression]: @ref JKQTPlotterBasicJKQTPDatastoreRegression "Regression Analysis (with the Statistics Library)"
[JKQTPlotterBasicJKQTPDatastoreStatisticsGroupedStat]: @ref JKQTPlotterBasicJKQTPDatastoreStatisticsGroupedStat "1-Dimensional Group Statistics with JKQTPDatastore"
[JKQTPlotterBasicJKQTPDatastoreStatistics2D]: @ref JKQTPlotterBasicJKQTPDatastoreStatistics2D "Advanced 2-Dimensional Statistics with JKQTPDatastore"
[statisticslibrary]: @ref jkqtptools_math_statistics "JKQTPlotter Statistics Library"
This tutorial project (see `./examples/simpletest_datastore/`) explains several options of JKQTPDatastore, which is the class used to centrally store the data for (most) graphs on a JKQTPlotter widget.
***Note*** that there are additional tutorial explaining other aspects of data mangement in JKQTPDatastore:
- [JKQTPlotterBasicJKQTPDatastore]
- [JKQTPlotterBasicJKQTPDatastoreIterators]
- [JKQTPlotterBasicJKQTPDatastoreStatistics]
- [JKQTPlotterBasicJKQTPDatastoreRegression]
- [JKQTPlotterBasicJKQTPDatastoreStatisticsGroupedStat]
- [JKQTPlotterBasicJKQTPDatastoreStatistics2D]
[TOC]
The source code of the main application can be found in [`jkqtplotter_simpletest_datastore.cpp`](https://github.com/jkriege2/JKQtPlotter/tree/master/examples/simpletest_datastore/jkqtplotter_simpletest_datastore.cpp).
@ -14,7 +32,7 @@ In every code-segment below, we will use these two declarations from the code to
JKQTPDatastore* datastore=plot.getDatastore();
```
# Copy Data from different data structures into JKQTPDatastore
# Copying Data from External COntainers into JKQTPDatastore
## Copy Data from a Vector into a column of the JKQTPDatastore
@ -118,7 +136,7 @@ In addition to the variants of `JKQTPDatastore::addColumn()`, that do not transf
It is also possible to leave the data mangement completely to the JKQTPDatastore and just edit the data with access functions from JKQTPDatastore.
## Generating Columns Non-Initialized Columns and Filling Them
## Generating Non-Initialized Columns and Filling Them
The most basic way to generate data for a plot is to generate two non-initialized columns for the x- and y-coordinates of the graph points
```.cpp
@ -177,6 +195,15 @@ You can use the methods `JKQTPDatastore::appendToColumn()` and `JKQTPDatastore::
```
Note that this operation changes the column length (number of rows). If the memory was externally managed before, it will be internally managed afterwards! If the first append is called on a column that cannot be extended, the contents will be copied and the column will reference the new, internally managed, memory afterwards.
Alterantively there is also a `std::back_inserter`-like interface to append to a column:
```.cpp
auto it=datastore->backInserter(columnID);
for (double ii=10; ii<=20; ii++) *++it=ii;
```
This, together with `JKQTPDatastore::begin()` and `JKQTPDatatstore::end()` allows to use `JKQTDatastore` together with algorithms from the C++ standard template libarary and other templated algorithms based on the same iterator-based interfaces (e.g. in boost).
## Using Data from one Column to Calculate Another
@ -300,3 +327,39 @@ The result will look like this (`JKQTPXYParametrizedScatterGraph` on the left an
![simpletest_datastore_image](https://raw.githubusercontent.com/jkriege2/JKQtPlotter/master/screenshots/simpletest_datastore_image.png)
# Iterator Interface
Some sections before we discussed a code-fragment
```.cpp
size_t itXColumn=datastore->addColumn(50, "cos curve: x-data");
size_t itYColumn=datastore->addColumn(50, "cos curve: y-data");
for (int i=0; i<datastore->getRows(itXColumn); i++) {
const double x=30.0+i/25.0*M_PI;
datastore->set(itXColumn, i, x);
datastore->set(itYColumn, i, cos(x));
}
```
in which an image was calculated pixel-by-pixel with explicit int indices. Alternatively you can also access the columns via C++ iterators:
```.cpp
auto itX=datastore->begin(colX))
auto itY=datastore->begin(colY))
for (; itX!=datastore->end(colX); ++itX, ++itY) {
const double x=double(i)/double(Ndata)*8.0*M_PI;
*itX=x;
*itY=sin(x);
}
```
All these code fragments result in the same graphs:
![simpletest_datastore_image](https://raw.githubusercontent.com/jkriege2/JKQtPlotter/master/screenshots/simpletest_datastore_image.png)
You can also use this interface to interface with algorithms e.g. from the C++ standard template library. E.g. if you want to sort the data in a column, you can simply call
```.cpp
std::sort(datastore->begin(colY), datastore->end(colY));
```
![simpletest_datastore_image_sorted](https://raw.githubusercontent.com/jkriege2/JKQtPlotter/master/screenshots/simpletest_datastore_image_sorted.png)
This, together with `JKQTPDatastore::backInserter()` allows to use `JKQTDatastore` together with algorithms from the C++ standard template libarary and other templated algorithms based on the same iterator-based interfaces (e.g. in boost).

View File

@ -8,7 +8,7 @@
#include "jkqtplotter/jkqtplotter.h"
#include "jkqtplotter/jkqtpgraphsscatter.h"
#include "jkqtplotter/jkqtpgraphsimage.h"
#include <algorithm>
int main(int argc, char* argv[])
{
@ -22,6 +22,11 @@ int main(int argc, char* argv[])
JKQTPColumnMathImage* imggraph;
JKQTPXYParametrizedScatterGraph* paramscattergraph;
/////////////////////////////////////////////////////////////////////////////////////////////////
/// externally provided data (i.e. from different containers)
/////////////////////////////////////////////////////////////////////////////////////////////////
// 2. first we create data inside a QVector for a simple plot (a sine curve) ... and add the plot
// note that you could use a std::vector equally well
QVector<double> X, Y;
@ -43,6 +48,7 @@ int main(int argc, char* argv[])
// 3. Now we generate a plot from data in a C-array, just reference in the JKQTPDatastore
// Note: JKQTPDatastore does not take ownership of your data!
#define NDATA 5
double XCA[NDATA]= { 1, 2, 3, 4, 5 };
double YCA[NDATA]= { 1, 0, 1, 0, 1 };
@ -70,6 +76,10 @@ int main(int argc, char* argv[])
/////////////////////////////////////////////////////////////////////////////////////////////////
/// internally managed data
/////////////////////////////////////////////////////////////////////////////////////////////////
// 5. It is also possible to leave the data mangement completely to the JKQTPDatastore
// and just edit the data with access functions from JKQTPDatastore:
plot.addGraph(linegraph=new JKQTPXYLineGraph(&plot));
@ -129,6 +139,7 @@ int main(int argc, char* argv[])
// 6. autoscale the plot so the graph is contained
plot.zoomToFit();

View File

@ -20,6 +20,7 @@ CONFIG (debug, debug|release) {
message("LIBS = $$LIBS")
win32-msvc*: DEFINES += _USE_MATH_DEFINES
win32-msvc*: DEFINES += NOMINMAX

View File

@ -0,0 +1,265 @@
# Tutorial (JKQTPDatastore): 1-Dimensional Group Statistics with JKQTPDatastore {#JKQTPlotterBasicJKQTPDatastoreStatisticsGroupedStat}
[JKQTPlotterBasicJKQTPDatastore]: @ref JKQTPlotterBasicJKQTPDatastore "Basic Usage of JKQTPDatastore"
[JKQTPlotterBasicJKQTPDatastoreIterators]: @ref JKQTPlotterBasicJKQTPDatastoreIterators "Iterator-Based usage of JKQTPDatastore"
[JKQTPlotterBasicJKQTPDatastoreStatistics]: @ref JKQTPlotterBasicJKQTPDatastoreStatistics "Advanced 1-Dimensional Statistics with JKQTPDatastore"
[JKQTPlotterBasicJKQTPDatastoreRegression]: @ref JKQTPlotterBasicJKQTPDatastoreRegression "Regression Analysis (with the Statistics Library)"
[JKQTPlotterBasicJKQTPDatastoreStatisticsGroupedStat]: @ref JKQTPlotterBasicJKQTPDatastoreStatisticsGroupedStat "1-Dimensional Group Statistics with JKQTPDatastore"
[JKQTPlotterBasicJKQTPDatastoreStatistics2D]: @ref JKQTPlotterBasicJKQTPDatastoreStatistics2D "Advanced 2-Dimensional Statistics with JKQTPDatastore"
[statisticslibrary]: @ref jkqtptools_math_statistics "JKQTPlotter Statistics Library"
This tutorial project (see `./examples/simpletest_datastore_groupedstat/`) explains several advanced functions of JKQTPDatastore in combination with the [[statisticslibrary]] conatined in JKQTPlotter.
***Note*** that there are additional tutorial explaining other aspects of data mangement in JKQTPDatastore:
- [JKQTPlotterBasicJKQTPDatastore]
- [JKQTPlotterBasicJKQTPDatastoreIterators]
- [JKQTPlotterBasicJKQTPDatastoreStatistics]
- [JKQTPlotterBasicJKQTPDatastoreRegression]
- [JKQTPlotterBasicJKQTPDatastoreStatisticsGroupedStat]
- [JKQTPlotterBasicJKQTPDatastoreStatistics2D]
[TOC]
The source code of the main application can be found in [`jkqtplotter_simpletest_datastore_groupedstat.cpp`](https://github.com/jkriege2/JKQtPlotter/tree/master/examples/simpletest_datastore_groupedstat/jkqtplotter_simpletest_datastore_groupedstat.cpp).
This tutorial cites only parts of this code to demonstrate different ways of working with data for the graphs.
# Barcharts & Boxplots from categorized data
## Generating a Dataset for Grouped Barcharts
To demonstrate the grouped statistics, we first have to generate a dataset. The datapoints consist of pairs `<group,value>`, where the groups are encoded by the numbers 1,2,3 and in each group, several measurements are taken:
```.cpp
size_t colBarRawGroup=datastore1->addColumn("barchart, rawdata, group");
size_t colBarRawValue=datastore1->addColumn("barchart, rawdata, value");
// data for group 1
datastore1->appendToColumns(colBarRawGroup, colBarRawValue, 1, 1.1);
datastore1->appendToColumns(colBarRawGroup, colBarRawValue, 1, 1.5);
datastore1->appendToColumns(colBarRawGroup, colBarRawValue, 1, 0.8);
// ...
// data for group 2
datastore1->appendToColumns(colBarRawGroup, colBarRawValue, 2, 2.2);
// ...
// data for group 3
datastore1->appendToColumns(colBarRawGroup, colBarRawValue, 3, 4.1);
// ...
```
Note that the data does not have to be sorted. You can add the dataset in any order!
This dataset can be visualized with a simple scatter plot:
```.cpp
JKQTPXYLineGraph* gScatterForBar;
plotbarchart->addGraph(gScatterForBar=new JKQTPXYLineGraph(plotbarchart));
gScatterForBar->setXYColumns(colBarRawGroup, colBarRawValue);
gScatterForBar->setDrawLine(false);
gScatterForBar->setSymbolType(JKQTPCross);
gScatterForBar->setSymbolSize(5);
gScatterForBar->setSymbolColor(QColorWithAlphaF(QColor("red"), 0.5));
```
The resulting plot looks like this:
![jkqtplotter_simpletest_datastore_groupedstat_barchartrawdata](https://raw.githubusercontent.com/jkriege2/JKQtPlotter/master/screenshots/jkqtplotter_simpletest_datastore_groupedstat_barchartrawdata.png)
## Calculating Grouped Statistics for a Barchart
Now we want to draw a barchart for every group, which indicates the average in each group. This is done using methods from the statistics library.
First we need to group the data using `jkqtpstatGroupData()`, which assembles the data points in each group groupeddataBar
```.cpp
std::map<double, std::vector<double> > groupeddataBar;
jkqtpstatGroupData(datastore1->begin(colBarRawGroup), datastore1->end(colBarRawGroup),
datastore1->begin(colBarRawValue), datastore1->end(colBarRawValue),
groupeddataBar);
```
Now we can calculate the statistics for each group separately: Data is collected in new columns `colBarGroup`, `colBarAverage` and `colBarStdDev`. The statistics is then calculated by simply iterating over `groupeddataBar` and calling functions like `jkqtpstatAverage()` for each group:
```.cpp
size_t colBarGroup=datastore1->addColumn("barchart, group");
size_t colBarAverage=datastore1->addColumn("barchart, group-average");
size_t colBarStdDev=datastore1->addColumn("barchart, group-stddev");
for (auto it=groupeddataBar.begin(); it!=groupeddataBar.end(); ++it) {
datastore1->appendToColumn(colBarGroup, it->first);
datastore1->appendToColumn(colBarAverage, jkqtpstatAverage(it->second.begin(), it->second.end()));
datastore1->appendToColumn(colBarStdDev, jkqtpstatStdDev(it->second.begin(), it->second.end()));
}
```
Finally the calculated groups are drawn:
```.cpp
JKQTPBarVerticalErrorGraph* gBar;
plotbarchart->addGraph(gBar=new JKQTPBarVerticalErrorGraph(plotbarchart));
gBar->setXYColumns(colBarGroup, colBarAverage);
gBar->setYErrorColumn(static_cast<int>(colBarStdDev));
```
The resulting plot looks like this:
![jkqtplotter_simpletest_datastore_groupedstat_barchart](https://raw.githubusercontent.com/jkriege2/JKQtPlotter/master/screenshots/jkqtplotter_simpletest_datastore_groupedstat_barchart.png)
In order to safe yo the typing of the code above, shortcuts in the form of adaptors exist:
```.cpp
jkqtpstatAddYErrorBarGraph(plotbarchart->getPlotter(),
datastore1->begin(colBarRawGroup), datastore1->end(colBarRawGroup),
datastore1->begin(colBarRawValue), datastore1->end(colBarRawValue));
```
Also other flavors exist that generate different graphs (see the JKQTPlotter documentation):
- `jkqtpstatAddYErrorLineGraph()` / `jkqtpstatAddXErrorLineGraph()`
- `jkqtpstatAddYErrorBarGraph()` / `jkqtpstatAddXErrorBarGraph()`
- `jkqtpstatAddYErrorImpulsesGraph()` / `jkqtpstatAddXErrorImpulsesGraph()`
- `jkqtpstatAddYErrorParametrizedScatterGraph()` / `jkqtpstatAddXErrorParametrizedScatterGraph()`
- `jkqtpstatAddYErrorFilledCurveGraph()` / `jkqtpstatAddXErrorFilledCurveGraph()`
- `jkqtpstatAddYErrorGraph()` / `jkqtpstatAddXErrorGraph()`
## Calculating Grouped Statistics for a Boxplot
With the methods above we can also calculate more advanced statistics, like e.g. boxplots:
```.cpp
size_t colBarMedian=datastore1->addColumn("barchart, group-median");
size_t colBarMin=datastore1->addColumn("barchart, group-min");
size_t colBarMax=datastore1->addColumn("barchart, group-max");
size_t colBarQ25=datastore1->addColumn("barchart, group-Q25");
size_t colBarQ75=datastore1->addColumn("barchart, group-Q75");
for (auto it=groupeddataBar.begin(); it!=groupeddataBar.end(); ++it) {
datastore1->appendToColumn(colBarMedian, jkqtpstatMedian(it->second.begin(), it->second.end()));
datastore1->appendToColumn(colBarMin, jkqtpstatMinimum(it->second.begin(), it->second.end()));
datastore1->appendToColumn(colBarMax, jkqtpstatMaximum(it->second.begin(), it->second.end()));
datastore1->appendToColumn(colBarQ25, jkqtpstatQuantile(it->second.begin(), it->second.end(), 0.25));
datastore1->appendToColumn(colBarQ75, jkqtpstatQuantile(it->second.begin(), it->second.end(), 0.75));
}
```
The result can be plotted using JKQTPBoxplotVerticalGraph, which receives a column for each value class of the final plot:
```.cpp
JKQTPBoxplotVerticalGraph* gBoxplot;
plotboxplot->addGraph(gBoxplot=new JKQTPBoxplotVerticalGraph(plotboxplot));
gBoxplot->setPositionColumn(colBarGroup);
gBoxplot->setMinColumn(colBarMin);
gBoxplot->setMaxColumn(colBarMax);
gBoxplot->setMedianColumn(colBarMedian);
gBoxplot->setPercentile25Column(colBarQ25);
gBoxplot->setPercentile75Column(colBarQ75);
```
The resulting plot looks like this:
![jkqtplotter_simpletest_datastore_groupedstat_boxplot](https://raw.githubusercontent.com/jkriege2/JKQtPlotter/master/screenshots/jkqtplotter_simpletest_datastore_groupedstat_boxplot.png)
In order to safe yo the typing of the code above, shortcuts in the form of adaptors exist:
```.cpp
jkqtpstatAddHBoxplotsAndOutliers(plotboxplot->getPlotter(),
datastore1->begin(colBarRawGroup), datastore1->end(colBarRawGroup),
datastore1->begin(colBarRawValue), datastore1->end(colBarRawValue));
```
Also other flavors exist that generate different graphs (see the JKQTPlotter documentation):
- `jkqtpstatAddVBoxplotsAndOutliers()` / `jkqtpstatAddHBoxplotsAndOutliers()`
- `jkqtpstatVAddBoxplots()` / `jkqtpstatHAddBoxplots()`
- `jkqtpstatAddBoxplots()`
# (Scatter-)Graphs with X/Y-errors from Categorized Data
## Dataset for XY Scatter Graphs
First we generate a second dataset, which is going to be used for a scaterplot. The datapoints consist of pairs `<x,y>`, that are based on a parabula with random deviations, both in x- and y-direction:
```.cpp
size_t colScatterRawX=datastore1->addColumn("scatterplot, rawdata, x");
size_t colScatterRawY=datastore1->addColumn("scatterplot, rawdata, y");
std::random_device rd; // random number generators:
std::mt19937 gen{rd()};
std::normal_distribution<> d1{0,0.5};
const size_t N=100;
const double xmax=3.5;
for (size_t i=0; i<N; i++) {
const double x=(static_cast<double>(i)-static_cast<double>(N)/2.0)*xmax/(static_cast<double>(N)/2.0);
const double y=jkqtp_sqr(x)+2.0;
datastore1->appendToColumns(colScatterRawX, colScatterRawY, x+d1(gen), y+d1(gen));
}
```
This dataset can be visualized:
```.cpp
JKQTPXYParametrizedScatterGraph* gScatterRaw;
plotscattererrors->addGraph(gScatterRaw=new JKQTPXYParametrizedScatterGraph(plotscattererrors));
gScatterRaw->setXYColumns(colScatterRawX, colScatterRawY);
gScatterRaw->setDrawLine(false);
gScatterRaw->setSymbolType(JKQTPCross);
gScatterRaw->setSymbolSize(5);
```
The resulting plot looks like this:
![jkqtplotter_simpletest_datastore_groupedstat_scatterrawdata](https://raw.githubusercontent.com/jkriege2/JKQtPlotter/master/screenshots/jkqtplotter_simpletest_datastore_groupedstat_scatterrawdata.png)
## Calculating x- and y-Errors from Categorized Data
Now we want to draw a scatterchart of the data, where data-points should be grouped together, in x-intervals of width 0.5. From all the points in each interval, we calculate the in both x- and y-direction the average and standard deviation. First we need to group the data using `jkqtpstatGroupData()`, which assembles the data points in each group groupeddataScatter. For the custom grouping of the datapoints we use the optional functor provided to `jkqtpstatGroupData()`: We use `jkqtpstatGroupingCustomRound1D()` with given parameters 0.25 for the (center) location of the first bin and bin width 0.5. The functor is not built by hand (which would be possible using std::bind), but with the generator function `jkqtpstatMakeGroupingCustomRound1D()`. In addition we use a variant of `jkqtpstatGroupData()`, which outputs a column with the category assigned to every data pair in the input data range:
```.cpp
std::map<double, std::pair<std::vector<double>,std::vector<double> > > groupeddataScatter;
size_t colScatterRawGroup=datastore1->addColumn("scatterplot, rawdata, assigned-group");
jkqtpstatGroupData(datastore1->begin(colScatterRawX), datastore1->end(colScatterRawX),
datastore1->begin(colScatterRawY), datastore1->end(colScatterRawY),
datastore1->backInserter(colScatterRawGroup),
groupeddataScatter,
jkqtpstatMakeGroupingCustomRound1D(0.25, 0.5));
```
The column colScatterRawGroup can now be used to color the scatter graph:
```.cpp
gScatterRaw->setColorColumn(colScatterRawGroup);
```
Now we can calculate the statistics for each group separately: Data is collected in two new columns. Then the statistics is calculated by simply iterating over `groupeddataScatter` and calling functions like `jkqtpstatAverage()` for each group:
```.cpp
size_t colScatterXAvg=datastore1->addColumn("scatter, x, average");
size_t colScatterXStd=datastore1->addColumn("scatter, x, stddev");
size_t colScatterYAvg=datastore1->addColumn("scatter, y, average");
size_t colScatterYStd=datastore1->addColumn("scatter, y, stddev");
for (auto it=groupeddataScatter.begin(); it!=groupeddataScatter.end(); ++it) {
datastore1->appendToColumn(colScatterXAvg, jkqtpstatAverage(it->second.first.begin(), it->second.first.end()));
datastore1->appendToColumn(colScatterXStd, jkqtpstatStdDev(it->second.first.begin(), it->second.first.end()));
datastore1->appendToColumn(colScatterYAvg, jkqtpstatAverage(it->second.second.begin(), it->second.second.end()));
datastore1->appendToColumn(colScatterYStd, jkqtpstatStdDev(it->second.second.begin(), it->second.second.end()));
}
```
Finally the calculated groups are drawn
```.cpp
JKQTPXYLineErrorGraph* gScatterErr;
plotscattererrors->addGraph(gScatterErr=new JKQTPXYLineErrorGraph(plotscattererrors));
gScatterErr->setXYColumns(colScatterXAvg, colScatterYAvg);
gScatterErr->setXErrorColumn(static_cast<int>(colScatterXStd));
gScatterErr->setYErrorColumn(static_cast<int>(colScatterYStd));
gScatterErr->setSymbolType(JKQTPFilledTriangle);
gScatterErr->setDrawLine(false);
```
The resulting plot looks like this:
![jkqtplotter_simpletest_datastore_groupedstat_scatter](https://raw.githubusercontent.com/jkriege2/JKQtPlotter/master/screenshots/jkqtplotter_simpletest_datastore_groupedstat_scatter.png)
In order to safe yo the typing of the code above, shortcuts in the form of adaptors exist:
```.cpp
jkqtpstatAddXYErrorLineGraph(plotscattererrors->getPlotter(),
datastore1->begin(colScatterRawX), datastore1->end(colScatterRawX),
datastore1->begin(colScatterRawY), datastore1->end(colScatterRawY),
jkqtpstatMakeGroupingCustomRound1D(0.25, 0.5));
```
Also other flavors exist that generate different graphs (see the JKQTPlotter documentation):
- `jkqtpstatAddXYErrorLineGraph()`
- `jkqtpstatAddXYErrorParametrizedScatterGraph()`
- `jkqtpstatAddXYErrorGraph()`
# Screenshot of the full Program
The output of the full test program [`jkqtplotter_simpletest_datastore_groupedstat.cpp`](https://github.com/jkriege2/JKQtPlotter/tree/master/examples/simpletest_datastore_groupedstat/jkqtplotter_simpletest_datastore_groupedstat.cpp) looks like this:
![jkqtplotter_simpletest_datastore_groupedstat](https://raw.githubusercontent.com/jkriege2/JKQtPlotter/master/screenshots/jkqtplotter_simpletest_datastore_groupedstat.png)

View File

@ -0,0 +1,265 @@
/** \example jkqtplotter_simpletest_datastore_groupedstat.cpp
* Explains how to use the internal statistics library (see \ref jkqtptools_statistics ) together with JKQTPDatastore to generate grouped statistics (i.e. calculates errorbars or boxplots from groups of datapoints in a x/y-dataset).
*
* \ref JKQTPlotterBasicJKQTPDatastoreStatisticsGroupedStat
*/
#include <QApplication>
#include "jkqtplotter/jkqtplotter.h"
#include "jkqtplotter/jkqtpgraphsscatter.h"
#include "jkqtplotter/jkqtpgraphsbarchart.h"
#include "jkqtplotter/jkqtpgraphsstatisticsadaptors.h"
#include "jkqtcommon/jkqtpstatisticstools.h"
#include "jkqtcommon/jkqtpstringtools.h"
#include <random>
#include <cmath>
#include <vector>
int main(int argc, char* argv[])
{
QApplication app(argc, argv);
// 1. create a window with several plotters and get a pointer to the internal datastores (for convenience)
QWidget mainWidget;
QGridLayout* lay;
mainWidget.setLayout(lay=new QGridLayout);
JKQTPlotter* plotbarchart=new JKQTPlotter(&mainWidget);
plotbarchart->getPlotter()->setPlotLabel("Barcharts");
JKQTPDatastore* datastore1=plotbarchart->getDatastore();
lay->addWidget(plotbarchart,0,0);
JKQTPlotter* plotboxplot=new JKQTPlotter(datastore1, &mainWidget);
plotboxplot->getPlotter()->setPlotLabel("Boxplots");
lay->addWidget(plotboxplot,0,1);
JKQTPlotter* plotscattererrors=new JKQTPlotter(datastore1, &mainWidget);
plotscattererrors->getPlotter()->setPlotLabel("Scatter Plot with Error Indicators");
lay->addWidget(plotscattererrors,0,2);
lay->setColumnStretch(0,1);
lay->setColumnStretch(1,1);
lay->setColumnStretch(2,2);
// 2. Barcharts from categorized data:
// 2.1. First we generate a dataset, which is going to be used for a barchart
// The datapoints consist of pairs <group,value>, where the groups are encoded
// by the numbers 1,2,3 and in each group, several measurements are taken
size_t colBarRawGroup=datastore1->addColumn("barchart, rawdata, group");
size_t colBarRawValue=datastore1->addColumn("barchart, rawdata, value");
// data for group 1
datastore1->appendToColumns(colBarRawGroup, colBarRawValue, 1, 1.1);
datastore1->appendToColumns(colBarRawGroup, colBarRawValue, 1, 1.5);
datastore1->appendToColumns(colBarRawGroup, colBarRawValue, 1, 0.8);
datastore1->appendToColumns(colBarRawGroup, colBarRawValue, 1, 1.2);
datastore1->appendToColumns(colBarRawGroup, colBarRawValue, 1, 1.4);
// data for group 2
datastore1->appendToColumns(colBarRawGroup, colBarRawValue, 2, 2.2);
datastore1->appendToColumns(colBarRawGroup, colBarRawValue, 2, 2.4);
datastore1->appendToColumns(colBarRawGroup, colBarRawValue, 2, 1.9);
datastore1->appendToColumns(colBarRawGroup, colBarRawValue, 2, 2.6);
datastore1->appendToColumns(colBarRawGroup, colBarRawValue, 2, 2.1);
// data for group 3
datastore1->appendToColumns(colBarRawGroup, colBarRawValue, 3, 4.1);
datastore1->appendToColumns(colBarRawGroup, colBarRawValue, 3, 4.4);
datastore1->appendToColumns(colBarRawGroup, colBarRawValue, 3, 3.8);
datastore1->appendToColumns(colBarRawGroup, colBarRawValue, 3, 4.5);
datastore1->appendToColumns(colBarRawGroup, colBarRawValue, 3, 3.7);
datastore1->appendToColumns(colBarRawGroup, colBarRawValue, 3, 4.0);
// now some more datapoint, in mixed order
datastore1->appendToColumns(colBarRawGroup, colBarRawValue, 1, 0.9);
datastore1->appendToColumns(colBarRawGroup, colBarRawValue, 2, 2.3);
datastore1->appendToColumns(colBarRawGroup, colBarRawValue, 2, 2.0);
datastore1->appendToColumns(colBarRawGroup, colBarRawValue, 1, 1.0);
datastore1->appendToColumns(colBarRawGroup, colBarRawValue, 3, 4.2);
datastore1->appendToColumns(colBarRawGroup, colBarRawValue, 1, 1.25);
datastore1->appendToColumns(colBarRawGroup, colBarRawValue, 2, 2.35);
datastore1->appendToColumns(colBarRawGroup, colBarRawValue, 3, 3.7);
datastore1->appendToColumns(colBarRawGroup, colBarRawValue, 1, 0.75);
datastore1->appendToColumns(colBarRawGroup, colBarRawValue, 2, 1.85);
datastore1->appendToColumns(colBarRawGroup, colBarRawValue, 3, 4.5);
datastore1->appendToColumns(colBarRawGroup, colBarRawValue, 1, 0.95);
datastore1->appendToColumns(colBarRawGroup, colBarRawValue, 2, 1.65);
datastore1->appendToColumns(colBarRawGroup, colBarRawValue, 3, 4.1);
datastore1->appendToColumns(colBarRawGroup, colBarRawValue, 1, 1.15);
datastore1->appendToColumns(colBarRawGroup, colBarRawValue, 2, 2.15);
// 2.2. This dataset can be visualized with a simple scatter plot:
JKQTPXYLineGraph* gScatterForBar;
plotbarchart->addGraph(gScatterForBar=new JKQTPXYLineGraph(plotbarchart));
gScatterForBar->setXYColumns(colBarRawGroup, colBarRawValue);
gScatterForBar->setDrawLine(false);
gScatterForBar->setSymbolType(JKQTPCross);
gScatterForBar->setSymbolSize(5);
gScatterForBar->setSymbolColor(QColorWithAlphaF(QColor("red"), 0.5));
// 2.3. Now we want to draw a barchart for every group, which indicates the
// average in each group. This is done using methods from the statistics
// library.
// First we need to group the data using jkqtpstatGroupData(), which assembles
// the data points in each group groupeddataBar
std::map<double, std::vector<double> > groupeddataBar;
jkqtpstatGroupData(datastore1->begin(colBarRawGroup), datastore1->end(colBarRawGroup),
datastore1->begin(colBarRawValue), datastore1->end(colBarRawValue),
groupeddataBar);
// now we can calculate the statistics for each group separately:
// Data is collected in two new columns
size_t colBarGroup=datastore1->addColumn("barchart, group");
size_t colBarAverage=datastore1->addColumn("barchart, group-average");
size_t colBarStdDev=datastore1->addColumn("barchart, group-stddev");
// Statistics is calculated by simply iterating over groupeddataBar
// and calling functions like jkqtpstatAverage() for each group
for (auto it=groupeddataBar.begin(); it!=groupeddataBar.end(); ++it) {
datastore1->appendToColumn(colBarGroup, it->first);
datastore1->appendToColumn(colBarAverage, jkqtpstatAverage(it->second.begin(), it->second.end()));
datastore1->appendToColumn(colBarStdDev, jkqtpstatStdDev(it->second.begin(), it->second.end()));
}
// 2.4. Finally the calculated groups are drawn
JKQTPBarVerticalErrorGraph* gBar;
plotbarchart->addGraph(gBar=new JKQTPBarVerticalErrorGraph(plotbarchart));
gBar->setXYColumns(colBarGroup, colBarAverage);
gBar->setYErrorColumn(static_cast<int>(colBarStdDev));
// 2.5. With the methods above we can also calculate more advanced statistics, like e.g. boxplots:
size_t colBarMedian=datastore1->addColumn("barchart, group-median");
size_t colBarMin=datastore1->addColumn("barchart, group-min");
size_t colBarMax=datastore1->addColumn("barchart, group-max");
size_t colBarQ25=datastore1->addColumn("barchart, group-Q25");
size_t colBarQ75=datastore1->addColumn("barchart, group-Q75");
for (auto it=groupeddataBar.begin(); it!=groupeddataBar.end(); ++it) {
datastore1->appendToColumn(colBarMedian, jkqtpstatMedian(it->second.begin(), it->second.end()));
datastore1->appendToColumn(colBarMin, jkqtpstatMinimum(it->second.begin(), it->second.end()));
datastore1->appendToColumn(colBarMax, jkqtpstatMaximum(it->second.begin(), it->second.end()));
datastore1->appendToColumn(colBarQ25, jkqtpstatQuantile(it->second.begin(), it->second.end(), 0.25));
datastore1->appendToColumn(colBarQ75, jkqtpstatQuantile(it->second.begin(), it->second.end(), 0.75));
}
// 2.6. The result can be plotted using JKQTPBoxplotVerticalGraph, which receives a column for each value class of the final plot:
JKQTPBoxplotVerticalGraph* gBoxplot;
plotboxplot->addGraph(gBoxplot=new JKQTPBoxplotVerticalGraph(plotboxplot));
gBoxplot->setPositionColumn(colBarGroup);
gBoxplot->setMinColumn(colBarMin);
gBoxplot->setMaxColumn(colBarMax);
gBoxplot->setMedianColumn(colBarMedian);
gBoxplot->setPercentile25Column(colBarQ25);
gBoxplot->setPercentile75Column(colBarQ75);
// 2.7. In order to safe yo the typing of the code above, shortcuts in the form of adaptors exist:
/*
jkqtpstatAddYErrorBarGraph(plotbarchart->getPlotter(),
datastore1->begin(colBarRawGroup), datastore1->end(colBarRawGroup),
datastore1->begin(colBarRawValue), datastore1->end(colBarRawValue));
jkqtpstatAddHBoxplotsAndOutliers(plotboxplot->getPlotter(),
datastore1->begin(colBarRawGroup), datastore1->end(colBarRawGroup),
datastore1->begin(colBarRawValue), datastore1->end(colBarRawValue));
*/
// 3. Scatterplots from categorized data:
// 3.1. First we generate a second dataset, which is going to be used for a scaterplot
// The datapoints consist of pairs <x,y>, that are based on a parabula with random
// deviations, both in x- and y-direction
size_t colScatterRawX=datastore1->addColumn("scatterplot, rawdata, x");
size_t colScatterRawY=datastore1->addColumn("scatterplot, rawdata, y");
std::random_device rd; // random number generators:
std::mt19937 gen{rd()};
std::normal_distribution<> d1{0,0.5};
const size_t N=100;
const double xmax=3.5;
for (size_t i=0; i<N; i++) {
const double x=(static_cast<double>(i)-static_cast<double>(N)/2.0)*xmax/(static_cast<double>(N)/2.0);
const double y=jkqtp_sqr(x)+2.0;
datastore1->appendToColumns(colScatterRawX, colScatterRawY, x+d1(gen), y+d1(gen));
}
// 3.2. Now we can also add the raw dataset to the plot for visualization:
JKQTPXYParametrizedScatterGraph* gScatterRaw;
plotscattererrors->addGraph(gScatterRaw=new JKQTPXYParametrizedScatterGraph(plotscattererrors));
gScatterRaw->setXYColumns(colScatterRawX, colScatterRawY);
gScatterRaw->setDrawLine(false);
gScatterRaw->setSymbolType(JKQTPCross);
gScatterRaw->setSymbolSize(5);
// 3.3. Now we want to draw a scatterchart of the data, where data-points should be grouped
// together, in x-intervals of width 0.5. From all the points in each interval, we calculate the
// in both x- and y-direction the average and standard deviation.
// First we need to group the data using jkqtpstatGroupData(), which assembles
// the data points in each group groupeddataScatter. For the custom grouping of the datapoints
// we use the optional functor provided to jkqtpstatGroupData(): We use jkqtpstatGroupingCustomRound1D()
// with given parameters 0.25 for the (center) location of the first bin and bin width 0.5. The functor
// is not built by hand (which would be possible using std::bind), but with the generator function
// jkqtpstatMakeGroupingCustomRound1D().
// in addition we use a variant of jkqtpstatGroupData(), which outputs a column with the category
// assigned to every data pair in the input data range
std::map<double, std::pair<std::vector<double>,std::vector<double> > > groupeddataScatter;
size_t colScatterRawGroup=datastore1->addColumn("scatterplot, rawdata, assigned-group");
jkqtpstatGroupData(datastore1->begin(colScatterRawX), datastore1->end(colScatterRawX),
datastore1->begin(colScatterRawY), datastore1->end(colScatterRawY),
datastore1->backInserter(colScatterRawGroup),
groupeddataScatter,
jkqtpstatMakeGroupingCustomRound1D(0.25, 0.5));
// The column colScatterRawGroup can now be used to color the scatter graph:
gScatterRaw->setColorColumn(colScatterRawGroup);
// now we can calculate the statistics for each group separately:
// Data is collected in two new columns
size_t colScatterXAvg=datastore1->addColumn("scatter, x, average");
size_t colScatterXStd=datastore1->addColumn("scatter, x, stddev");
size_t colScatterYAvg=datastore1->addColumn("scatter, y, average");
size_t colScatterYStd=datastore1->addColumn("scatter, y, stddev");
// Statistics is calculated by simply iterating over groupeddataScatter
// and calling functions like jkqtpstatAverage() for each group
for (auto it=groupeddataScatter.begin(); it!=groupeddataScatter.end(); ++it) {
datastore1->appendToColumn(colScatterXAvg, jkqtpstatAverage(it->second.first.begin(), it->second.first.end()));
datastore1->appendToColumn(colScatterXStd, jkqtpstatStdDev(it->second.first.begin(), it->second.first.end()));
datastore1->appendToColumn(colScatterYAvg, jkqtpstatAverage(it->second.second.begin(), it->second.second.end()));
datastore1->appendToColumn(colScatterYStd, jkqtpstatStdDev(it->second.second.begin(), it->second.second.end()));
}
// 3.4. Finally the calculated groups are drawn
JKQTPXYLineErrorGraph* gScatterErr;
plotscattererrors->addGraph(gScatterErr=new JKQTPXYLineErrorGraph(plotscattererrors));
gScatterErr->setXYColumns(colScatterXAvg, colScatterYAvg);
gScatterErr->setXErrorColumn(static_cast<int>(colScatterXStd));
gScatterErr->setYErrorColumn(static_cast<int>(colScatterYStd));
gScatterErr->setSymbolType(JKQTPFilledTriangle);
gScatterErr->setDrawLine(false);
// 3.5. also here an adaptor exists, which makes the task easier:
/*
jkqtpstatAddXYErrorLineGraph(plotscattererrors->getPlotter(),
datastore1->begin(colScatterRawX), datastore1->end(colScatterRawX),
datastore1->begin(colScatterRawY), datastore1->end(colScatterRawY),
jkqtpstatMakeGroupingCustomRound1D(0.25, 0.5));
*/
// autoscale the plot so the graph is contained
plotboxplot->synchronizeToMaster(plotbarchart, JKQTBasePlotter::sdXYAxes);
plotboxplot->zoomToFit();
plotboxplot->setGrid(false);
plotboxplot->setShowZeroAxes(false);
plotboxplot->getPlotter()->setKeyBackgroundColor(QColorWithAlphaF("white", 0.25), Qt::SolidPattern);
plotbarchart->setAbsoluteY(0,5);
plotboxplot->setAbsoluteY(0,5);
plotbarchart->zoomToFit();
plotbarchart->setGrid(false);
plotbarchart->setShowZeroAxes(false);
plotbarchart->getPlotter()->setKeyBackgroundColor(QColorWithAlphaF("white", 0.25), Qt::SolidPattern);
plotbarchart->moveGraphTop(gScatterForBar);
plotscattererrors->zoomToFit();
plotscattererrors->setGrid(false);
plotscattererrors->setShowZeroAxes(false);
plotscattererrors->getPlotter()->setKeyBackgroundColor(QColorWithAlphaF("white", 0.25), Qt::SolidPattern);
// show plotter and make it a decent size
mainWidget.show();
mainWidget.resize(1200,400);
return app.exec();
}

View File

@ -0,0 +1,27 @@
# source code for this simple demo
SOURCES = jkqtplotter_simpletest_datastore_groupedstat.cpp
# configure Qt
CONFIG += link_prl qt
QT += core gui xml svg
greaterThan(QT_MAJOR_VERSION, 4): QT += widgets printsupport
# output executable name
TARGET = jkqtplotter_simpletest_datastore_groupedstat
# include JKQTPlotter source headers and link against library
DEPENDPATH += ../../lib ../../staticlib/jkqtplotterlib
INCLUDEPATH += ../../lib
CONFIG (debug, debug|release) {
LIBS += -L../../staticlib/jkqtplotterlib/debug -ljkqtplotterlib_debug
} else {
LIBS += -L../../staticlib/jkqtplotterlib/release -ljkqtplotterlib
}
message("LIBS = $$LIBS")
win32-msvc*: DEFINES += _USE_MATH_DEFINES
win32-msvc*: DEFINES += NOMINMAX

View File

@ -0,0 +1,8 @@
TEMPLATE = subdirs
SUBDIRS += jkqtplotterlib jkqtplotter_simpletest_datastore_groupedstat
jkqtplotterlib.file = ../../staticlib/jkqtplotterlib/jkqtplotterlib.pro
jkqtplotter_simpletest_datastore_groupedstat.file=$$PWD/jkqtplotter_simpletest_datastore_groupedstat.pro
jkqtplotter_simpletest_datastore_groupedstat.depends = jkqtplotterlib

View File

@ -0,0 +1,114 @@
# Tutorial (JKQTPDatastore): Iterator-Based usage of JKQTPDatastore {#JKQTPlotterBasicJKQTPDatastoreIterators}
[JKQTPlotterBasicJKQTPDatastore]: @ref JKQTPlotterBasicJKQTPDatastore "Basic Usage of JKQTPDatastore"
[JKQTPlotterBasicJKQTPDatastoreIterators]: @ref JKQTPlotterBasicJKQTPDatastoreIterators "Iterator-Based usage of JKQTPDatastore"
[JKQTPlotterBasicJKQTPDatastoreStatistics]: @ref JKQTPlotterBasicJKQTPDatastoreStatistics "Advanced 1-Dimensional Statistics with JKQTPDatastore"
[JKQTPlotterBasicJKQTPDatastoreRegression]: @ref JKQTPlotterBasicJKQTPDatastoreRegression "Regression Analysis (with the Statistics Library)"
[JKQTPlotterBasicJKQTPDatastoreStatisticsGroupedStat]: @ref JKQTPlotterBasicJKQTPDatastoreStatisticsGroupedStat "1-Dimensional Group Statistics with JKQTPDatastore"
[JKQTPlotterBasicJKQTPDatastoreStatistics2D]: @ref JKQTPlotterBasicJKQTPDatastoreStatistics2D "Advanced 2-Dimensional Statistics with JKQTPDatastore"
[statisticslibrary]: @ref jkqtptools_math_statistics "JKQTPlotter Statistics Library"
This tutorial project (see `./examples/simpletest_datastore_iterators/`) explains how to use the iterator-based interface to JKQTPDatastore.
***Note*** that there are additional tutorial explaining other aspects of data mangement in JKQTPDatastore:
- [JKQTPlotterBasicJKQTPDatastore]
- [JKQTPlotterBasicJKQTPDatastoreIterators]
- [JKQTPlotterBasicJKQTPDatastoreStatistics]
- [JKQTPlotterBasicJKQTPDatastoreRegression]
- [JKQTPlotterBasicJKQTPDatastoreStatisticsGroupedStat]
- [JKQTPlotterBasicJKQTPDatastoreStatistics2D]
[TOC]
The source code of the main application can be found in [`jkqtplotter_simpletest_datastore_iterators.cpp`](https://github.com/jkriege2/JKQtPlotter/tree/master/examples/simpletest_datastore_iterators/jkqtplotter_simpletest_datastore_iterators.cpp).
This tutorial cites parts of this code to demonstrate different ways of working with JKQTPDatastore's iterator-interface.
In every code-segment below, we will use these two declarations from the code to access the internal datastore of the JKQTPlotter instance:
```.cpp
// 1. create a plotter window and get a pointer to the internal datastore (for convenience)
JKQTPlotter plot;
JKQTPDatastore* datastore=plot.getDatastore();
```
In the example [JKQTPlotterBasicJKQTPDatastore] we discussed how to copy data from external container into and explicitly access data in columns inside a JKQTPDatastore. This tutorial explains how to use the iterator interface of JKQTPDatastore to access the data, build columns and also interact with algorithms from the C++ standard template library (or other iterator-based libraries, like e.g. boost). Also have a look at the [statisticslibrary] and [JKQTPlotterBasicJKQTPDatastoreStatistics], as these also use the iterator-interface of JKQTPDatastore.
# Iterator-based Column Data Access To Existing Rows
In other tutorials we used e.g. `JKQTPDatastore::set()` to set values in data columns. Using this scheme, you can write code like shown below to draw a cose curve:
```.cpp
size_t XCol=datastore->addLinearColumn(50, 0, 4.0*M_PI, "cos curve: x-data");
size_t YCol=datastore->addColumn(datastore->getRows(XCol), "cos curve: y-data");
for (size_t i=0; i<datastore->getRows(XCol); i++) {
datastore->set(YCol, i, cos(datastore->get(XCol, i)));
}
```
Here we added two columns with 50 entries. `XCol` contains linearly spaced values between 0 and 2*pi and `YCol` contains 50 uninitialized values. Then we iterate an index `i` over all these items (`datastore->getRows(XCol)` returns the rows in a column, i.e. 50 in the example above) and used `JKQTPDatastore::set()` to store the calculated values in the two columns. The current x-values is read from `XCol` using `JKQTPDatastore::get()`. The resulting plot looks like this:
![simpletest_datastore_iterators_cosine](https://raw.githubusercontent.com/jkriege2/JKQtPlotter/master/screenshots/simpletest_datastore_iterators_cosine.png)
The same loop can be written using iterators:
```.cpp
size_t XCol=datastore->addLinearColumn(50, 0, 4.0*M_PI, "cos curve: x-data");
size_t YCol=datastore->addColumn(datastore->getRows(XCol), "cos curve: y-data");
auto itY=datastore->begin(YCol);
for (auto itX=datastore->begin(XCol); itX!=datastore->end(XCol); ++itX, ++itY) {
*itY=cos(*itX);
}
```
# Back-Inserter for Columns
Above we used two previously sized columns and accessed (read and writing) existing rows in them. But `JKQTPDatastore` also provides an iterator comparable to [`std::back_inserter`](https://de.cppreference.com/w/cpp/iterator/back_inserter), which allows to add rows at the end of an existing (here initially empty) column:
```.cpp
size_t XCol=datastore->addColumn("cos curve: x-data");
size_t YCol=datastore->addColumn("cos curve: y-data");
auto biXCol=datastore->backInserter(XCol);
auto biYCol=datastore->backInserter(YCol);
for (double x=0; x<4.0*M_PI; x+=4.0*M_PI/50.0) {
*++biXCol=x;
*++biYCol=cos(x);
}
```
# Using C++ STL algorithms
You can write this a bit more compact, if you use `JKQTPDatastore::addLinearColumn()` and the C++ STL-algorithm [`std::transform()`](https://de.cppreference.com/w/cpp/algorithm/transform):
```.cpp
size_t XCol=datastore->addLinearColumn(50, 0, 4.0*M_PI, "cos curve: x-data");
size_t YCol=datastore->addColumn("cos curve: y-data");
std::transform(datastore->begin(XCol), datastore->end(XCol), datastore->backInserter(YCol), &cos);
```
Of course you can now also interface other algorithms, like e.g. [`std::sort()`](https://de.cppreference.com/w/cpp/algorithm/sort):
```.cpp
std::sort(datastore->begin(colY), datastore->end(colY));
```
With this line of code, the `YCol` is sorted in ascending order and the plot becomes:
![simpletest_datastore_iterators_cosine_ysorted](https://raw.githubusercontent.com/jkriege2/JKQtPlotter/master/screenshots/simpletest_datastore_iterators_cosine_ysorted.png)
Another example would be to replace all value `y<-0.5` with the value `1.0` using [`std::replace_if()`](https://de.cppreference.com/w/cpp/algorithm/replace):
```.cpp
std::replace_if(datastore->begin(YCol), datastore->end(YCol), [](double v) { return v<-0.5; }, 1.0);
```
![simpletest_datastore_iterators_cosine_yreplaced](https://raw.githubusercontent.com/jkriege2/JKQtPlotter/master/screenshots/simpletest_datastore_iterators_cosine_yreplaced.png)
Finally also the [erase-remove idiom](https://en.wikipedia.org/wiki/Erase%E2%80%93remove_idiom) (e.g. known from `std::vector`) is supported:
```.cpp
datastore->eraseFromColumn(std::remove_if(datastore->begin(YCol), datastore->end(YCol), [](double v) { return v<0; }), datastore->end(YCol));
```
# Special Properties of the JKQTPDatastore-Iterators
Note that the iterator classes of `JKQTPDatastore` (namely `JKQTPColumnIterator` and `JKQTPColumnConstIterator`) provide additional function to access the properties of the data-column row they point to:
- `JKQTPColumnIterator::isValid()` checks whether the iterator points to a valid row in a column. it is `false` e.g. for an iterator returned by `JKQTPDatastore::end()`
- `JKQTPColumnIterator::getPosition()` returns the row/position inside the column the iterator points to
- `JKQTPColumnIterator::getImagePosition()` / `JKQTPColumnIterator::getImagePositionX()` / `JKQTPColumnIterator::getImagePositionY()` return the x-/y-location of the pointed-to pixel in an image column
- `JKQTPColumnIterator::getImageColumns()` / `JKQTPColumnIterator::getImageRows()` return the width/height of the image represented by the image column (the pointed-to pixel is part of)

View File

@ -0,0 +1,72 @@
/** \example jkqtplotter_simpletest_datastore_iterators.cpp
* Iterator-base data Management in JKQTPDatastore.
*
* \ref JKQTPlotterBasicJKQTPDatastoreIterators
*/
#include <QApplication>
#include "jkqtplotter/jkqtplotter.h"
#include "jkqtplotter/jkqtpgraphsscatter.h"
#include "jkqtplotter/jkqtpgraphsimage.h"
#include <algorithm>
int main(int argc, char* argv[])
{
QApplication app(argc, argv);
// 1. create a plotter window and get a pointer to the internal datastore (for convenience)
JKQTPlotter plot;
JKQTPDatastore* datastore=plot.getDatastore();
JKQTPXYLineGraph* linegraph;
// 2. now we create a cos-curve plot with the standard means of JKQTPDatastore::addColumn(NRows, "name")
// and JKQTPDatastore::set() to actually set the values:
//size_t XCol=datastore->addLinearColumn(50, 0, 4.0*M_PI, "cos curve: x-data");
//size_t YCol=datastore->addColumn(datastore->getRows(XCol), "cos curve: y-data");
//for (size_t i=0; i<datastore->getRows(XCol); i++) {
// datastore->set(YCol, i, cos(datastore->get(XCol, i)));
//}
// 2.1. the for-loop above can also be expressed in terms of (random-access) iterators:
//auto itY=datastore->begin(YCol);
//for (auto itX=datastore->begin(XCol); itX!=datastore->end(XCol); ++itX, ++itY) {
// *itY=cos(*itX);
//}
// 2.2 with backInserter() you can generate an object liek a std::back_inserter, which
// removes the requirement to pre-allocate the columns:
size_t XCol=datastore->addColumn("cos curve: x-data");
size_t YCol=datastore->addColumn("cos curve: y-data");
auto biXCol=datastore->backInserter(XCol);
auto biYCol=datastore->backInserter(YCol);
for (double x=0; x<4.0*M_PI; x+=4.0*M_PI/50.0) {
*++biXCol=x;
*++biYCol=cos(x);
}
// 2.3 combining with addLinearColumn() you can also use C++ STL algorithms:
//size_t XCol=datastore->addLinearColumn(50, 0, 4.0*M_PI, "cos curve: x-data");
//size_t YCol=datastore->addColumn("cos curve: y-data");
//std::transform(datastore->begin(XCol), datastore->end(XCol), datastore->backInserter(YCol), cos);
// 2.4. Just for fun we can now sort the data:
//std::sort(datastore->begin(YCol), datastore->end(YCol));
// or replace any value <-0.5 with 1:
//std::replace_if(datastore->begin(YCol), datastore->end(YCol), [](double v) { return v<-0.5; }, 1.0);
// 2.5. Also the erase-remove idiom is supported:
//datastore->eraseFromColumn(std::remove_if(datastore->begin(YCol), datastore->end(YCol), [](double v) { return v<0; }), datastore->end(YCol));
plot.addGraph(linegraph=new JKQTPXYLineGraph(&plot));
linegraph->setXColumn(XCol);
linegraph->setYColumn(YCol);
linegraph->setTitle(QObject::tr("cosine graph"));
// 6. autoscale the plot so the graph is contained
plot.zoomToFit();
// show plotter and make it a decent size
plot.show();
plot.resize(600,400);
return app.exec();
}

View File

@ -0,0 +1,27 @@
# source code for this simple demo
SOURCES = jkqtplotter_simpletest_datastore_iterators.cpp
# configure Qt
CONFIG += link_prl qt
QT += core gui xml svg
greaterThan(QT_MAJOR_VERSION, 4): QT += widgets printsupport
# output executable name
TARGET = jkqtplotter_simpletest_datastore_iterators
# include JKQTPlotter source headers and link against library
DEPENDPATH += ../../lib ../../staticlib/jkqtplotterlib
INCLUDEPATH += ../../lib
CONFIG (debug, debug|release) {
LIBS += -L../../staticlib/jkqtplotterlib/debug -ljkqtplotterlib_debug
} else {
LIBS += -L../../staticlib/jkqtplotterlib/release -ljkqtplotterlib
}
message("LIBS = $$LIBS")
win32-msvc*: DEFINES += _USE_MATH_DEFINES
win32-msvc*: DEFINES += NOMINMAX

View File

@ -0,0 +1,8 @@
TEMPLATE = subdirs
SUBDIRS += jkqtplotterlib jkqtplotter_simpletest_datastore_iterators
jkqtplotterlib.file = ../../staticlib/jkqtplotterlib/jkqtplotterlib.pro
jkqtplotter_simpletest_datastore_iterators.file=$$PWD/jkqtplotter_simpletest_datastore_iterators.pro
jkqtplotter_simpletest_datastore_iterators.depends = jkqtplotterlib

View File

@ -0,0 +1,370 @@
# Tutorial (JKQTPDatastore): Regression Analysis (with the Statistics Library) {#JKQTPlotterBasicJKQTPDatastoreRegression}
[JKQTPlotterBasicJKQTPDatastore]: @ref JKQTPlotterBasicJKQTPDatastore "Basic Usage of JKQTPDatastore"
[JKQTPlotterBasicJKQTPDatastoreIterators]: @ref JKQTPlotterBasicJKQTPDatastoreIterators "Iterator-Based usage of JKQTPDatastore"
[JKQTPlotterBasicJKQTPDatastoreStatistics]: @ref JKQTPlotterBasicJKQTPDatastoreStatistics "Advanced 1-Dimensional Statistics with JKQTPDatastore"
[JKQTPlotterBasicJKQTPDatastoreRegression]: @ref JKQTPlotterBasicJKQTPDatastoreRegression "Regression Analysis (with the Statistics Library)"
[JKQTPlotterBasicJKQTPDatastoreStatisticsGroupedStat]: @ref JKQTPlotterBasicJKQTPDatastoreStatisticsGroupedStat "1-Dimensional Group Statistics with JKQTPDatastore"
[JKQTPlotterBasicJKQTPDatastoreStatistics2D]: @ref JKQTPlotterBasicJKQTPDatastoreStatistics2D "Advanced 2-Dimensional Statistics with JKQTPDatastore"
[statisticslibrary]: @ref jkqtptools_math_statistics "JKQTPlotter Statistics Library"
This tutorial project (see `./examples/simpletest_datastore_statistics/`) explains several advanced functions of JKQTPDatastore in combination with the [[statisticslibrary]] conatined in JKQTPlotter.
***Note*** that there are additional tutorial explaining other aspects of data mangement in JKQTPDatastore:
- [JKQTPlotterBasicJKQTPDatastore]
- [JKQTPlotterBasicJKQTPDatastoreIterators]
- [JKQTPlotterBasicJKQTPDatastoreStatistics]
- [JKQTPlotterBasicJKQTPDatastoreRegression]
- [JKQTPlotterBasicJKQTPDatastoreStatisticsGroupedStat]
- [JKQTPlotterBasicJKQTPDatastoreStatistics2D]
[TOC]
The source code of the main application can be found in [`jkqtplotter_simpletest_datastore_regression.cpp`](https://github.com/jkriege2/JKQtPlotter/tree/master/examples/simpletest_datastore_statistics/jkqtplotter_simpletest_datastore_regression.cpp).
This tutorial cites only parts of this code to demonstrate different ways of performing regression analysis.
# Simple Linear Regression
First we generate a set of datapoints (x,y), which scatter randomly around a linear function.
```.cpp
std::random_device rd; // random number generators:
std::mt19937 gen{rd()};
std::normal_distribution<> d1{0,1};
double a0=-5;
double b0=2;
size_t colLinX=datastore1->addColumn("lin data, x");
size_t colLinY=datastore1->addColumn("lin data, y");
for (double x=-5; x<=10; x++) {
datastore1->appendToColumn(colLinX, x);
datastore1->appendToColumn(colLinY, a0+b0*x+d1(gen));
}
```
... and we visualize this data with a simple scatter graph:
```.cpp
JKQTPXYLineGraph* graphD;
plot1->addGraph(graphD=new JKQTPXYLineGraph(plot1));
graphD->setXYColumns(colLinX, colLinY);
graphD->setDrawLine(false);
graphD->setTitle("data");
```
Now we can caluate the regression line (i.e. the two regression coefficients a and b of the function \c f(x)=a+b*x) using the function `jkqtpstatLinearRegression()` from the [statisticslibrary]:
```.cpp
double coeffA=0, coeffB=0;
jkqtpstatLinearRegression(datastore1->begin(colLinX), datastore1->end(colLinX), datastore1->begin(colLinY), datastore1->end(colLinY), coeffA, coeffB);
```
... and add a `JKQTPXFunctionLineGraph` to draw the resulting linear function:
```.cpp
JKQTPXFunctionLineGraph *graphRegLine=new JKQTPXFunctionLineGraph(plot1);
graphRegLine->setSpecialFunction(JKQTPXFunctionLineGraph::SpecialFunction::Line);
graphRegLine->setParamsV(coeffA, coeffB);
graphRegLine->setTitle(QString("regression: $f(x) = %1 + %2 \\cdot x$").arg(jkqtp_floattolatexqstr(coeffA)).arg(jkqtp_floattolatexqstr(coeffB)));
plot1->addGraph(graphRegLine);
```
These two steps can be simplified using an "adaptor":
```.cpp
jkqtpstatAddLinearRegression(plot1->getPlotter(), datastore1->begin(colLinX), datastore1->end(colLinX), datastore1->begin(colLinY), datastore1->end(colLinY));
```
... or even shorter:
```.cpp
jkqtpstatAddLinearRegression(graphD);
```
Here the x- and y-columns from the `JKQTPXYGraph`-based graph `graphD` (see above) are used as datasources for the plot.
The plot resulting from any of the variants above looks like this:
![jkqtplotter_simpletest_datastore_regression_lin](https://raw.githubusercontent.com/jkriege2/JKQtPlotter/master/screenshots/jkqtplotter_simpletest_datastore_regression_lin.png)
# Robust Linear Regression
Sometimes data contains outliers that can render the results of a regression analysis inaccurate. For such cases the [statisticslibrary] offers the function `jkqtpstatRobustIRLSLinearRegression()`, which is a drop-in replacement for `jkqtpstatLinearRegression()` and solves the optimization problem a) in the Lp-norm (which is more robust to outliers) and b) uses the [iteratively reweighted least-squares algorithm (IRLS)](https://en.wikipedia.org/wiki/Iteratively_reweighted_least_squares), which performs a series of regressions, where in each instance the data-points are weighted differently. The method assigns a lower weight to those points that are far from the current best-fit (typically the outliers) and thus slowly comes nearer to an estimate that is not distorted by the outliers.
To demonstrate this method, we use the same dataset as above, but add a few outliers:
```.cpp
std::random_device rd; // random number generators:
std::mt19937 gen{rd()};
std::normal_distribution<> d1{0,1};
double a0=-5;
double b0=2;
size_t colLinX=datastore1->addColumn("lin data, x");
size_t colLinY=datastore1->addColumn("lin data, y");
for (double x=-5; x<=10; x++) {
datastore1->appendToColumn(colLinX, x);
if (jkqtp_approximatelyEqual(x, -5)||jkqtp_approximatelyEqual(x, -3)) datastore1->appendToColumn(colRLinY, a0+b0*x+d1(gen)+12);
else datastore1->appendToColumn(colRLinY, a0+b0*x+d1(gen));
}
```
Note the outliers ar x=-5 and x=-3!
With this dataset we can use the same code as above, but with `jkqtpstatRobustIRLSLinearRegression()` instead of `jkqtpstatLinearRegression()`:
```.cpp
double coeffA=0, coeffB=0;
jkqtpstatRobustIRLSLinearRegression(datastore1->begin(colRLinX), datastore1->end(colRLinX), datastore1->begin(colRLinY), datastore1->end(colRLinY), coeffA, coeffB);
JKQTPXFunctionLineGraph *graphRegLine=new JKQTPXFunctionLineGraph(plot3);
graphRegLine->setSpecialFunction(JKQTPXFunctionLineGraph::SpecialFunction::Line);
graphRegLine->setParamsV(coeffA, coeffB);
graphRegLine->setTitle(QString("robust regression: $f(x) = %1 + %2 \\cdot x$").arg(jkqtp_floattolatexqstr(coeffA)).arg(jkqtp_floattolatexqstr(coeffB)));
plot3->addGraph(graphRegLine);
```
Also for the robust regression, there are two shortcuts in the form of "adaptors":
```.cpp
jkqtpstatAddRobustIRLSLinearRegression(plot3->getPlotter(), datastore1->begin(colRLinX), datastore1->end(colRLinX), datastore1->begin(colRLinY), datastore1->end(colRLinY));
```
and
```.cpp
jkqtpstatAddRobustIRLSLinearRegression(graphD);
```
The following screenshot shows the result of the IRLS regression analysis and for comparison the normal linear regression for the same dataset (plotted using `jkqtpstatAddLinearRegression(graphD);`):
![jkqtplotter_simpletest_datastore_regression_linrobust](https://raw.githubusercontent.com/jkriege2/JKQtPlotter/master/screenshots/jkqtplotter_simpletest_datastore_regression_linrobust.png)
The following screenshot shows the influence of the regularization parameter p (default value 1.1) onto the fit result:
- the closer `p` is to 1, the more robust the fit is (it is closer to the L1-norm)
- the closer `p` is to 2, the closer the fit is to the least squares solution (i.e. the normal regression obtained with the L2 norm)
![jkqtplotter_simpletest_datastore_regression_linrobust_p](https://raw.githubusercontent.com/jkriege2/JKQtPlotter/master/screenshots/jkqtplotter_simpletest_datastore_regression_linrobust_p.png)
# Weighted Linear Regression
Another option to react to measurement errors is to take these into account when calculating the regression. To do so, you can use weighted regression that uses the measurement errors as inverse weights. This algorithm is implemented in the function `jkqtpstatLinearWeightedRegression()`.
First we generate again a set of datapoints (x,y), which scatter randomly around a linear function. In addition we calculate an "error" `err` for each datapoint:
```.cpp
std::random_device rd; // random number generators:
std::mt19937 gen{rd()};
std::uniform_real_distribution<> de{0.5,1.5};
std::uniform_int_distribution<> ddecide{0,4};
double a0=-5;
double b0=2;
size_t colWLinX=datastore1->addColumn("wlin data, x");
size_t colWLinY=datastore1->addColumn("wlin data, y");
size_t colWLinE=datastore1->addColumn("wlin data, errors");
for (double x=-5; x<=10; x++) {
double factor=1;
if (ddecide(gen)==4) {
factor=4;
}
const double err=de(gen)*factor;
datastore1->appendToColumn(colWLinX, x);
datastore1->appendToColumn(colWLinY, a0+b0*x+err);
datastore1->appendToColumn(colWLinE, 1.0/err);
}
```
We use distribution `de` to draw deviations from the ideal linear function from the range 0.5...1.5. then - for good measure - we use a second distribution `ddecide` (dice tossing) to select a few datapoints to have a 4-fold increased error.
Finally we visualize this data with a simple scatter graph with error indicators:
```.cpp
JKQTPXYLineErrorGraph* graphE;
plot2->addGraph(graphE=new JKQTPXYLineErrorGraph(plot2));
graphE->setXYColumns(colWLinX, colWLinY);
graphE->setYErrorColumn(static_cast<int>(colWLinE));
graphE->setDrawLine(false);
graphE->setTitle("data");
```
Now we can caluate the regression line (i.e. the two regression coefficients a and b of the function \c f(x)=a+b*x) using the function `jkqtpstatLinearWeightedRegression()` from the [statisticslibrary]:
```.cpp
double coeffA=0, coeffB=0;
jkqtpstatLinearWeightedRegression(datastore1->begin(colWLinX), datastore1->end(colWLinX),
datastore1->begin(colWLinY), datastore1->end(colWLinY),
datastore1->begin(colWLinE), datastore1->end(colWLinE),
coeffA, coeffB, false, false,
&jkqtp_inversePropSaveDefault<double>);
```
***Note*** that in addition to the three data-columns we also provided a C++ functor `jkqtp_inversePropSaveDefault()`, which calculates 1/error. This is done, because the function `jkqtpstatLinearWeightedRegression()` uses the data from the range `datastore1->begin(colWLinE)` ... `datastore1->end(colWLinE)` directly as weights, but we calculated errors, which are inversely proportional to the weight of each data point when solving the least squares problem, as data points with larger errors should be weighted less than thos with smaller errors (outliers).
Again these two steps can be simplified using an "adaptor":
```.cpp
jkqtpstatAddLinearWeightedRegression(plot1->getPlotter(),
datastore1->begin(colLinX), datastore1->end(colLinX),
datastore1->begin(colLinY), datastore1->end(colLinY),
datastore1->begin(colWLinE), datastore1->end(colWLinE),
&coeffA, &coeffB, false, false,
&jkqtp_inversePropSaveDefault<double>);
```
... or even shorter:
```.cpp
jkqtpstatAddLinearWeightedRegression(graphD);
```
Here the x- and y-columns from the `JKQTPXYGraph`-based graph `graphE` (see above) and the weights from the error column of `graphE` are used as datasources for the plot. This function implicitly uses the function `jkqtp_inversePropSaveDefault()` to convert plot errors to weights, as it is already clear that we are dealing with errors rather than direct weights.
The plot resulting from any of the variants above looks like this:
![jkqtplotter_simpletest_datastore_regression_linweight](https://raw.githubusercontent.com/jkriege2/JKQtPlotter/master/screenshots/jkqtplotter_simpletest_datastore_regression_linweight.png)
For this plot we also added a call to
```.cpp
jkqtpstatAddLinearRegression(graphE);
```
which performs a simple non-weighted regression. The difference between the two resulting linear functions (blue: simple regression, green: weighted regression) demonstrates the influence of the weighting.
# Linearizable Regression Models
In addition to the simple linear regression model `f(x)=a+b*x`, it is also possible to fit a few non-linear models by transforming the data:
- power-law function`f(x)=a*x^b`, which is a linear function in a log(x)-log(y)-plot
- exponential function `f(x)=a*exp(b*x)`, which is a linear function in a x-log(y)-plot
- logarithm function `f(x)=a+b*ln(x)`, which is a linear function in a exp(x)-y-plot
The available models are defined in the enum `JKQTPStatRegressionModelType`. And there exists a function `jkqtpStatGenerateRegressionModel()`, which returns a C++-functor representing the function.
To demonstrate these fitting options, we first generate data from an exponential and a power-law model. Note that we also add normally distributed errors, but in order to ensure that we do not obtain y-values <0, we use loops that draw normally distributed random numbers, until this condition is met:
```.cpp
std::random_device rd; // random number generators:
std::mt19937 gen{rd()};
std::normal_distribution<> d1{0,1};
double a0_powerlaw=20;
double b0_powerlaw=0.25;
double a0_exp=5;
double b0_exp=0.5;
size_t colNLLinX=datastore1->addColumn("non-lin data, x");
size_t colNLLinYExp=datastore1->addColumn("non-lin data, y, exponential model");
size_t colNLLinYPow=datastore1->addColumn("non-lin data, y, power-law model");
auto model_powerlaw=jkqtpStatGenerateRegressionModel(JKQTPStatRegressionModelType::PowerLaw);
auto model_exp=jkqtpStatGenerateRegressionModel(JKQTPStatRegressionModelType::Exponential);
for (double x=0.1; x<=10; x+=0.5) {
datastore1->appendToColumn(colNLLinX, x);
double ypow=model_powerlaw(x, a0_powerlaw, b0_powerlaw)+d1(gen);
while (ypow<0) {
ypow=model_powerlaw(x, a0_powerlaw, b0_powerlaw)+d1(gen);
}
datastore1->appendToColumn(colNLLinYPow, ypow);
double yexp=model_exp(x, a0_exp, b0_exp)+d1(gen);
while (yexp<0) {
yexp=model_exp(x, a0_exp, b0_exp)+d1(gen);
}
datastore1->appendToColumn(colNLLinYExp, yexp);
}
```
The generated data is visualized with scatter-plots:
```.cpp
JKQTPXYLineGraph* graphD_powerlaw;
plot5->addGraph(graphD_powerlaw=new JKQTPXYLineGraph(plot5));
graphD_powerlaw->setXYColumns(colNLLinX, colNLLinYPow);
graphD_powerlaw->setDrawLine(false);
graphD_powerlaw->setTitle(QString("data $%1+\\mathcal{N}(0,1)$").arg(jkqtpstatRegressionModel2Latex(JKQTPStatRegressionModelType::PowerLaw, a0_powerlaw, b0_powerlaw)));
JKQTPXYLineGraph* graphD_exp;
plot4->addGraph(graphD_exp=new JKQTPXYLineGraph(plot4));
graphD_exp->setXYColumns(colNLLinX, colNLLinYExp);
graphD_exp->setDrawLine(false);
graphD_exp->setTitle(QString("data $%1+\\mathcal{N}(0,1)$").arg(jkqtpstatRegressionModel2Latex(JKQTPStatRegressionModelType::Exponential, a0_exp, b0_exp)));
```
Now we can fit the regression models using `jkqtpstatRegression()`, which receives the model type as first parameter:
```.cpp
double cA=0, cB=0;
JKQTPXFunctionLineGraph* gFunc;
jkqtpstatRegression(JKQTPStatRegressionModelType::Exponential, datastore1->begin(colNLLinX), datastore1->end(colNLLinX), datastore1->begin(colNLLinYExp), datastore1->end(colNLLinYExp), cA, cB);
plot4->addGraph(gFunc=new JKQTPXFunctionLineGraph(plot4));
gFunc->setPlotFunctionFunctor(jkqtpStatGenerateRegressionModel(JKQTPStatRegressionModelType::Exponential, cA, cB));
gFunc->setTitle(QString("regression: $%1$").arg(jkqtpstatRegressionModel2Latex(JKQTPStatRegressionModelType::Exponential, cA, cB)));
cA=0; cB=0;
jkqtpstatRegression(JKQTPStatRegressionModelType::PowerLaw, datastore1->begin(colNLLinX), datastore1->end(colNLLinX), datastore1->begin(colNLLinYPow), datastore1->end(colNLLinYPow), cA, cB);
plot5->addGraph(gFunc=new JKQTPXFunctionLineGraph(plot5));
gFunc->setPlotFunctionFunctor(jkqtpStatGenerateRegressionModel(JKQTPStatRegressionModelType::PowerLaw, cA, cB));
gFunc->setTitle(QString("regression: $%1$").arg(jkqtpstatRegressionModel2Latex(JKQTPStatRegressionModelType::PowerLaw, cA, cB)));
```
The regression models can be plotted using a `JKQTPXFunctionLineGraph`. the fucntion to plot is again generated by calling `jkqtpStatGenerateRegressionModel()`, but now with the parameters determined above the respective lines. Note that `jkqtpstatRegressionModel2Latex()` outputs the model as LaTeX string, which can be used as plot label.
The resulting plot looks like this:
![jkqtplotter_simpletest_datastore_regression_nonlinreg](https://raw.githubusercontent.com/jkriege2/JKQtPlotter/master/screenshots/jkqtplotter_simpletest_datastore_regression_nonlinreg.png)
Of course also "adaptors" exist that allow to perform the steps above in a single function call:
```.cpp
jkqtpstatAddRegression(plot4->getPlotter(), JKQTPStatRegressionModelType::Exponential, datastore1->begin(colNLLinX), datastore1->end(colNLLinX), datastore1->begin(colNLLinYExp), datastore1->end(colNLLinYExp));
jkqtpstatAddRegression(plot5->getPlotter(), JKQTPStatRegressionModelType::PowerLaw, datastore1->begin(colNLLinX), datastore1->end(colNLLinX), datastore1->begin(colNLLinYPow), datastore1->end(colNLLinYPow));
```
... or even shorter:
```.cpp
jkqtpstatAddRegression(graphD_exp, JKQTPStatRegressionModelType::Exponential);
jkqtpstatAddRegression(graphD_powerlaw, JKQTPStatRegressionModelType::PowerLaw);
```
Also note that we used the function `jkqtpstatRegression()` above, which performs a linear regression (internally uses `jkqtpstatLinearRegression()`). But there also exist variants for robust IRLS regression adn weighted regression:
- `jkqtpstatRobustIRLSRegression()` / `jkqtpstatAddRobustIRLSRegression()`
- `jkqtpstatWeightedRegression()` / `jkqtpstatAddWeightedRegression()`
# Polynom Fitting
Finally the [statisticslibrary] also supports one option for non-linear model fitting, namely fitting of polynomial models. This is implemented in the function `jkqtpstatPolyFit()`.
To demonstrate this function we first generate data from a poylnomial model (with gaussian noise):
```.cpp
std::random_device rd; // random number generators:
std::mt19937 gen{rd()};
std::normal_distribution<> d1{0,50};
std::vector<double> pPoly {1,2,-2,0.5};
size_t colPolyX=datastore1->addColumn("polynomial data, x");
size_t colPolyY=datastore1->addColumn("polynomial data, y");
for (double x=-10; x<=10; x++) {
datastore1->appendToColumn(colPolyX, x);
datastore1->appendToColumn(colPolyY, jkqtp_polyEval(x, pPoly.begin(), pPoly.end())+d1(gen));
}
```
The function `jkqtp_polyEval()` is used to evaluate a given polynomial (coefficients in `pPoly`) at a position `x`.
The generated data is visualized with scatter-plots:
```.cpp
JKQTPXYLineGraph* graphP;
plot6->addGraph(graphP=new JKQTPXYLineGraph(plot6));
graphP->setXYColumns(colPolyX, colPolyY);
graphP->setDrawLine(false);
graphP->setTitle(QString("data $%1+\\mathcal{N}(0,50)$").arg(jkqtp_polynomialModel2Latex(pPoly.begin(), pPoly.end())));
```
Here the function `jkqtp_polynomialModel2Latex()` generates a string from a polynomial model.
Now we can call `jkqtpstatPolyFit()` to fit different polynomial regression models to the data:
```.cpp
for (size_t p=0; p<=5; p++) {
std::vector<double> pFit;
JKQTPXFunctionLineGraph* gPoly;
jkqtpstatPolyFit(datastore1->begin(colPolyX), datastore1->end(colPolyX), datastore1->begin(colPolyY), datastore1->end(colPolyY), p, std::back_inserter(pFit));
plot6->addGraph(gPoly=new JKQTPXFunctionLineGraph(plot6));
gPoly->setPlotFunctionFunctor(jkqtp_generatePolynomialModel(pFit.begin(), pFit.end()));
gPoly->setTitle(QString("regression: $%1$").arg(jkqtp_polynomialModel2Latex(pFit.begin(), pFit.end())));
}
```
Each model is also ploted using a `JKQTPXFunctionLineGraph`. The plot function assigned to these `JKQTPXFunctionLineGraph` is generated by calling `jkqtp_generatePolynomialModel()`, which returns a C++-functor for a polynomial.
The resulting plots look like this (without added gaussian noise):
![jkqtplotter_simpletest_datastore_regression_polynom](https://raw.githubusercontent.com/jkriege2/JKQtPlotter/master/screenshots/jkqtplotter_simpletest_datastore_regression_polynom.png)
... and with added gaussian noise:
![jkqtplotter_simpletest_datastore_regression_polynom_errros](https://raw.githubusercontent.com/jkriege2/JKQtPlotter/master/screenshots/jkqtplotter_simpletest_datastore_regression_polynom_errros.png)
Of course also the "adaptor" shortcuts are available:
```.cpp
for (size_t p=0; p<=5; p++) {
jkqtpstatAddPolyFit(plot6->getPlotter(), datastore1->begin(colPolyX), datastore1->end(colPolyX), datastore1->begin(colPolyY), datastore1->end(colPolyY), p);
jkqtpstatAddPolyFit(graphP, p);
}
```
# Screenshot of the full Program
The output of the full test program [`jkqtplotter_simpletest_datastore_regression.cpp`](https://github.com/jkriege2/JKQtPlotter/tree/master/examples/simpletest_datastore_statistics/jkqtplotter_simpletest_datastore_regression.cpp) looks like this:
![jkqtplotter_simpletest_datastore_regression](https://raw.githubusercontent.com/jkriege2/JKQtPlotter/master/screenshots/jkqtplotter_simpletest_datastore_regression.png)

View File

@ -0,0 +1,338 @@
/** \example jkqtplotter_simpletest_datastore_regression.cpp
* Explains how to use the internal statistics library (see \ref jkqtptools_statistics ) together with JKQTPDatastore to perform different types of regression and polynomial fitting.
*
* \ref JKQTPlotterBasicJKQTPDatastoreRegression
*/
#include <QApplication>
#include "jkqtplotter/jkqtplotter.h"
#include "jkqtplotter/jkqtpgraphspeakstream.h"
#include "jkqtplotter/jkqtpgraphsboxplot.h"
#include "jkqtplotter/jkqtpgraphsstatisticsadaptors.h"
#include "jkqtplotter/jkqtpgraphsevaluatedfunction.h"
#include "jkqtcommon/jkqtpstatisticstools.h"
#include "jkqtcommon/jkqtpstringtools.h"
#include <random>
#include <cmath>
int main(int argc, char* argv[])
{
QApplication app(argc, argv);
// 1. create a window with several plotters and get a pointer to the internal datastores (for convenience)
QWidget mainWidget;
QGridLayout* lay;
mainWidget.setLayout(lay=new QGridLayout);
JKQTPlotter* plot1=new JKQTPlotter(&mainWidget);
plot1->getPlotter()->setPlotLabel("Simple Linear Regression");
JKQTPDatastore* datastore1=plot1->getDatastore();
lay->addWidget(plot1,0,0);
JKQTPlotter *plot2=new JKQTPlotter(datastore1, &mainWidget);
plot2->getPlotter()->setPlotLabel("Weighted Linear Regression");
lay->addWidget(plot2,1,0);
JKQTPlotter* plot3=new JKQTPlotter(datastore1, &mainWidget);
plot3->getPlotter()->setPlotLabel("Robust Linear Regression");
lay->addWidget(plot3,0,1);
JKQTPlotter *plot6=new JKQTPlotter(datastore1, &mainWidget);
plot6->getPlotter()->setPlotLabel("Polynomial Fitting");
lay->addWidget(plot6,1,1);
JKQTPlotter* plot4=new JKQTPlotter(datastore1, &mainWidget);
plot4->getPlotter()->setPlotLabel("Exponential Regression");
lay->addWidget(plot4,0,2);
JKQTPlotter* plot5=new JKQTPlotter(datastore1, &mainWidget);
plot5->getPlotter()->setPlotLabel("Power-Law Regression");
lay->addWidget(plot5,1,2);
// 2.1. To demonstrate linear regression, we create a dataset with a linear dependence between two
// columns and added gaussian noise
std::random_device rd; // random number generators:
std::mt19937 gen{rd()};
std::normal_distribution<> d1{0,1};
double a0=-5;
double b0=2;
size_t colLinX=datastore1->addColumn("lin data, x");
size_t colLinY=datastore1->addColumn("lin data, y");
for (double x=-5; x<=10; x++) {
datastore1->appendToColumn(colLinX, x);
datastore1->appendToColumn(colLinY, a0+b0*x+d1(gen));
}
// we visualize this data with a simple scatter graph:
JKQTPXYLineGraph* graphD;
plot1->addGraph(graphD=new JKQTPXYLineGraph(plot1));
graphD->setXYColumns(colLinX, colLinY);
graphD->setDrawLine(false);
graphD->setTitle(QString("data $f(x)=%1+%2\\cdot x+\\mathcal{N}(0,1)$").arg(jkqtp_floattolatexqstr(a0,1)).arg(jkqtp_floattolatexqstr(b0,1)));
// 2.2. Now we calculate the regression line and add a plot to the graph:
/*double coeffA=0, coeffB=0;
jkqtpstatLinearRegression(datastore1->begin(colLinX), datastore1->end(colLinX), datastore1->begin(colLinY), datastore1->end(colLinY), coeffA, coeffB);
JKQTPXFunctionLineGraph *graphRegLine=new JKQTPXFunctionLineGraph(plot1);
graphRegLine->setSpecialFunction(JKQTPXFunctionLineGraph::SpecialFunction::Line);
graphRegLine->setParamsV(coeffA, coeffB);
graphRegLine->setTitle(QString("regression: $f(x) = %1 + %2 \\cdot x$").arg(jkqtp_floattolatexqstr(coeffA)).arg(jkqtp_floattolatexqstr(coeffB)));
plot1->addGraph(graphRegLine);*/
// this code can also be written with one function call, using the "adaptor" jkqtpstatAddLinearRegression():
//jkqtpstatAddLinearRegression(plot1->getPlotter(), datastore1->begin(colLinX), datastore1->end(colLinX), datastore1->begin(colLinY), datastore1->end(colLinY));
// or even shorter:
jkqtpstatAddLinearRegression(graphD);
// 3.1. We extend the example above by
//std::random_device rd; // random number generators:
//std::mt19937 gen{rd()};
std::uniform_real_distribution<> de{0.5,1.5};
std::uniform_int_distribution<> ddecide{0,4};
//double a0=-5;
//double b0=2;
size_t colWLinX=datastore1->addColumn("wlin data, x");
size_t colWLinY=datastore1->addColumn("wlin data, y");
size_t colWLinE=datastore1->addColumn("wlin data, errors");
for (double x=-5; x<=10; x++) {
double factor=1;
if (ddecide(gen)==4) {
factor=4;
}
const double err=de(gen)*factor;
datastore1->appendToColumn(colWLinX, x);
datastore1->appendToColumn(colWLinY, a0+b0*x+err);
datastore1->appendToColumn(colWLinE, err);
}
// we visualize this data with a simple scatter graph:
JKQTPXYLineErrorGraph* graphE;
plot2->addGraph(graphE=new JKQTPXYLineErrorGraph(plot2));
graphE->setXYColumns(colWLinX, colWLinY);
graphE->setYErrorColumn(static_cast<int>(colWLinE));
graphE->setDrawLine(false);
graphE->setTitle(QString("data $f(x)=%1+%2\\cdot x+\\mbox{Noise}$").arg(jkqtp_floattolatexqstr(a0,1)).arg(jkqtp_floattolatexqstr(b0,1)));
// 2.2. Now we calculate the regression line and add a plot to the graph:
/*double coeffA=0, coeffB=0;
jkqtpstatLinearWeightedRegression(datastore1->begin(colWLinX), datastore1->end(colWLinX),
datastore1->begin(colWLinY), datastore1->end(colWLinY),
datastore1->begin(colWLinE), datastore1->end(colWLinE),
coeffA, coeffB, false, false,
&jkqtp_inversePropSaveDefault<double>);
// note that in addition to the three data-columns we also provided a C++ functor
// jkqtp_inversePropSaveDefault(), which calculates 1/error. This is done, because the function
// jkqtpstatLinearWeightedRegression() uses the data from the range datastore1->begin(colWLinE) ... datastore1->end(colWLinE)
// directly as weights, but we calculated errors, which are inversely proportional to the
// weight of each data point when solving the least squares problem, as data points with
// larger errors should be weighted less than thos with smaller errors
//
// Now we can plot the resulting linear function:
JKQTPXFunctionLineGraph *graphRegLine=new JKQTPXFunctionLineGraph(plot2);
graphRegLine->setSpecialFunction(JKQTPXFunctionLineGraph::SpecialFunction::Line);
graphRegLine->setParamsV(coeffA, coeffB);
graphRegLine->setTitle(QString("weighted regression: $f(x) = %1 + %2 \\cdot x$").arg(jkqtp_floattolatexqstr(coeffA)).arg(jkqtp_floattolatexqstr(coeffB)));
plot2->addGraph(graphRegLine);*/
// this code can also be written with one function call, using the "adaptor" jkqtpstatAddLinearRegression():
//jkqtpstatAddLinearWeightedRegression(plot2->getPlotter(),
// datastore1->begin(colLinX), datastore1->end(colLinX),
// datastore1->begin(colLinY), datastore1->end(colLinY),
// datastore1->begin(colWLinE), datastore1->end(colWLinE),
// &coeffA, &coeffB, false, false,
// &jkqtp_inversePropSaveDefault<double>);
// or even shorter:
jkqtpstatAddLinearWeightedRegression(graphE);
// to demonstrate the effect of the weighting, we also add a simple linear regression that
// does not take into account the errors:
jkqtpstatAddLinearRegression(graphE);
// 4.1. To demonstrate IRLS linear regression, we create a dataset with a linear dependence between two
// columns and added gaussian noise and some outliers
//std::random_device rd; // random number generators:
//std::mt19937 gen{rd()};
//std::normal_distribution<> d1{0,1};
//double a0=-5;
//double b0=2;
size_t colRLinX=datastore1->addColumn("lin data, x");
size_t colRLinY=datastore1->addColumn("lin data, y");
for (double x=-5; x<=10; x++) {
datastore1->appendToColumn(colRLinX, x);
if (jkqtp_approximatelyEqual(x, -5)||jkqtp_approximatelyEqual(x, -3)) datastore1->appendToColumn(colRLinY, a0+b0*x+d1(gen)+12);
else datastore1->appendToColumn(colRLinY, a0+b0*x+d1(gen));
}
// we visualize this data with a simple scatter graph:
//JKQTPXYLineGraph* graphD;
plot3->addGraph(graphD=new JKQTPXYLineGraph(plot3));
graphD->setXYColumns(colRLinX, colRLinY);
graphD->setDrawLine(false);
graphD->setTitle(QString("data $f(x)=%1+%2\\cdot x+\\mathcal{N}(0,1)$").arg(jkqtp_floattolatexqstr(a0,1)).arg(jkqtp_floattolatexqstr(b0,1)));
// 4.2. Now we calculate the regression line and add a plot to the graph:
double coeffA=0, coeffB=0;
jkqtpstatRobustIRLSLinearRegression(datastore1->begin(colRLinX), datastore1->end(colRLinX), datastore1->begin(colRLinY), datastore1->end(colRLinY), coeffA, coeffB);
JKQTPXFunctionLineGraph *graphRegLine=new JKQTPXFunctionLineGraph(plot3);
graphRegLine->setSpecialFunction(JKQTPXFunctionLineGraph::SpecialFunction::Line);
graphRegLine->setParamsV(coeffA, coeffB);
graphRegLine->setTitle(QString("robust regression: $f(x) = %1 + %2 \\cdot x$, $p=1.1$").arg(jkqtp_floattolatexqstr(coeffA)).arg(jkqtp_floattolatexqstr(coeffB)));
plot3->addGraph(graphRegLine);
// this code can also be written with one function call, using the "adaptor" jkqtpstatAddLinearRegression():
//jkqtpstatAddRobustIRLSLinearRegression(plot3->getPlotter(), datastore1->begin(colRLinX), datastore1->end(colRLinX), datastore1->begin(colRLinY), datastore1->end(colRLinY));
// or even shorter:
//jkqtpstatAddRobustIRLSLinearRegression(graphD);
// as a comparison, we also add the result of the normal/non-robust linear regression:
jkqtpstatAddLinearRegression(graphD);
// the following code demonstrates the influence of the rgularization parameter p:
// - the closer it is to 1, the more robust the fit is (it is closer to the L1-norm)
// - the closer it is to 2, the closer the fit is to the least squares solution (i.e. the normal regression)
double p;
p=1.1;
auto g=jkqtpstatAddRobustIRLSLinearRegression(graphD, nullptr, nullptr, false, false, p);
g->setTitle(g->getTitle()+", $p="+jkqtp_floattolatexqstr(p)+"$");
p=1.5;
g=jkqtpstatAddRobustIRLSLinearRegression(graphD, nullptr, nullptr, false, false, p);
g->setTitle(g->getTitle()+", $p="+jkqtp_floattolatexqstr(p)+"$");
p=1.7;
g=jkqtpstatAddRobustIRLSLinearRegression(graphD, nullptr, nullptr, false, false, p);
g->setTitle(g->getTitle()+", $p="+jkqtp_floattolatexqstr(p)+"$");
p=2;
g=jkqtpstatAddRobustIRLSLinearRegression(graphD, nullptr, nullptr, false, false, p);
g->setTitle(g->getTitle()+", $p="+jkqtp_floattolatexqstr(p)+"$");
// 5.1. The functions for linear regression can also be used to calculate some non-linear models by transforming the input data.
// This is also supported by the statistics library. the supported models are defined in JKQTPStatRegressionModelType
//std::random_device rd; // random number generators:
//std::mt19937 gen{rd()};
//std::normal_distribution<> d1{0,1};
double a0_powerlaw=20;
double b0_powerlaw=0.25;
double a0_exp=5;
double b0_exp=0.5;
double a0_log=0;
double b0_log=1;
size_t colNLLinX=datastore1->addColumn("non-lin data, x");
size_t colNLLinYExp=datastore1->addColumn("non-lin data, y, exponential model");
size_t colNLLinYPow=datastore1->addColumn("non-lin data, y, power-law model");
size_t colNLLinYLog=datastore1->addColumn("non-lin data, y, log-law model");
auto model_powerlaw=jkqtpStatGenerateRegressionModel(JKQTPStatRegressionModelType::PowerLaw);
auto model_exp=jkqtpStatGenerateRegressionModel(JKQTPStatRegressionModelType::Exponential);
auto model_log=jkqtpStatGenerateRegressionModel(JKQTPStatRegressionModelType::Logarithm);
for (double x=0.1; x<=10; x+=0.5) {
datastore1->appendToColumn(colNLLinX, x);
double ypow=model_powerlaw(x, a0_powerlaw, b0_powerlaw)+d1(gen);
while (ypow<0) {
ypow=model_powerlaw(x, a0_powerlaw, b0_powerlaw)+d1(gen);
}
datastore1->appendToColumn(colNLLinYPow, ypow);
double yexp=model_exp(x, a0_exp, b0_exp)+d1(gen);
while (yexp<0) {
yexp=model_exp(x, a0_exp, b0_exp)+d1(gen);
}
datastore1->appendToColumn(colNLLinYExp, yexp);
datastore1->appendToColumn(colNLLinYLog, model_log(x, a0_log, b0_log));
}
// we visualize this data with a simple scatter graphs:
JKQTPXYLineGraph* graphD_powerlaw;
plot5->addGraph(graphD_powerlaw=new JKQTPXYLineGraph(plot5));
graphD_powerlaw->setXYColumns(colNLLinX, colNLLinYPow);
graphD_powerlaw->setDrawLine(false);
graphD_powerlaw->setTitle(QString("data $%1+\\mathcal{N}(0,1)$").arg(jkqtpstatRegressionModel2Latex(JKQTPStatRegressionModelType::PowerLaw, a0_powerlaw, b0_powerlaw)));
JKQTPXYLineGraph* graphD_exp;
plot4->addGraph(graphD_exp=new JKQTPXYLineGraph(plot4));
graphD_exp->setXYColumns(colNLLinX, colNLLinYExp);
graphD_exp->setDrawLine(false);
graphD_exp->setTitle(QString("data $%1+\\mathcal{N}(0,1)$").arg(jkqtpstatRegressionModel2Latex(JKQTPStatRegressionModelType::Exponential, a0_exp, b0_exp)));
JKQTPXYLineGraph* graphD_log;
plot5->addGraph(graphD_log=new JKQTPXYLineGraph(plot5));
graphD_log->setXYColumns(colNLLinX, colNLLinYLog);
graphD_log->setDrawLine(false);
graphD_log->setTitle(QString("data $%1+\\mathcal{N}(0,1)$").arg(jkqtpstatRegressionModel2Latex(JKQTPStatRegressionModelType::Logarithm, a0_log, b0_log)));
// 5.2. Now we calculate the regression models and add a plot to the graph:
double cA=0, cB=0;
JKQTPXFunctionLineGraph* gFunc;
jkqtpstatRegression(JKQTPStatRegressionModelType::Exponential, datastore1->begin(colNLLinX), datastore1->end(colNLLinX), datastore1->begin(colNLLinYExp), datastore1->end(colNLLinYExp), cA, cB);
plot4->addGraph(gFunc=new JKQTPXFunctionLineGraph(plot4));
gFunc->setPlotFunctionFunctor(jkqtpStatGenerateRegressionModel(JKQTPStatRegressionModelType::Exponential, cA, cB));
gFunc->setTitle(QString("regression: $%1$").arg(jkqtpstatRegressionModel2Latex(JKQTPStatRegressionModelType::Exponential, cA, cB)));
cA=0; cB=0;
jkqtpstatRegression(JKQTPStatRegressionModelType::PowerLaw, datastore1->begin(colNLLinX), datastore1->end(colNLLinX), datastore1->begin(colNLLinYPow), datastore1->end(colNLLinYPow), cA, cB);
plot5->addGraph(gFunc=new JKQTPXFunctionLineGraph(plot5));
gFunc->setPlotFunctionFunctor(jkqtpStatGenerateRegressionModel(JKQTPStatRegressionModelType::PowerLaw, cA, cB));
gFunc->setTitle(QString("regression: $%1$").arg(jkqtpstatRegressionModel2Latex(JKQTPStatRegressionModelType::PowerLaw, cA, cB)));
// Note: Here we used the normal linear regression functions, but variants for IRLS and weighted regression are also available!
// 5.3. Of course also adaptors exist:
//jkqtpstatAddRegression(plot4->getPlotter(), JKQTPStatRegressionModelType::Exponential, datastore1->begin(colNLLinX), datastore1->end(colNLLinX), datastore1->begin(colNLLinYExp), datastore1->end(colNLLinYExp));
//jkqtpstatAddRegression(plot5->getPlotter(), JKQTPStatRegressionModelType::PowerLaw, datastore1->begin(colNLLinX), datastore1->end(colNLLinX), datastore1->begin(colNLLinYPow), datastore1->end(colNLLinYPow));
//jkqtpstatAddRegression(graphD_exp, JKQTPStatRegressionModelType::Exponential);
//jkqtpstatAddRegression(graphD_powerlaw, JKQTPStatRegressionModelType::PowerLaw);
jkqtpstatAddRegression(graphD_log, JKQTPStatRegressionModelType::Logarithm);
// 6.1. To demonstrate polynomial fitting, we generate data for a polynomial model
std::vector<double> pPoly {1,2,-2,0.5};
size_t colPolyX=datastore1->addColumn("polynomial data, x");
size_t colPolyY=datastore1->addColumn("polynomial data, y");
for (double x=-10; x<=10; x++) {
datastore1->appendToColumn(colPolyX, x);
datastore1->appendToColumn(colPolyY, jkqtp_polyEval(x, pPoly.begin(), pPoly.end())+d1(gen)*50.0);
}
// we visualize this data with a simple scatter graph:
JKQTPXYLineGraph* graphP;
plot6->addGraph(graphP=new JKQTPXYLineGraph(plot6));
graphP->setXYColumns(colPolyX, colPolyY);
graphP->setDrawLine(false);
graphP->setTitle(QString("data $%1+\\mathcal{N}(0,50)$").arg(jkqtp_polynomialModel2Latex(pPoly.begin(), pPoly.end())));
// 6.2. now we can fit polynomials with different number of coefficients:
for (size_t p=0; p<=5; p++) {
std::vector<double> pFit;
JKQTPXFunctionLineGraph* gPoly;
jkqtpstatPolyFit(datastore1->begin(colPolyX), datastore1->end(colPolyX), datastore1->begin(colPolyY), datastore1->end(colPolyY), p, std::back_inserter(pFit));
plot6->addGraph(gPoly=new JKQTPXFunctionLineGraph(plot6));
gPoly->setPlotFunctionFunctor(jkqtp_generatePolynomialModel(pFit.begin(), pFit.end()));
gPoly->setTitle(QString("regression: $%1$").arg(jkqtp_polynomialModel2Latex(pFit.begin(), pFit.end())));
}
// 6.3. of course also the "adaptor" shortcuts are available:
//for (size_t p=0; p<=5; p++) {
// jkqtpstatAddPolyFit(plot6->getPlotter(), datastore1->begin(colPolyX), datastore1->end(colPolyX), datastore1->begin(colPolyY), datastore1->end(colPolyY), p);
// jkqtpstatAddPolyFit(graphP, p);
//}
// autoscale the plot so the graph is contained
plot1->zoomToFit();
plot1->getXAxis()->setShowZeroAxis(false);
plot1->getYAxis()->setShowZeroAxis(false);
plot1->getPlotter()->setKeyPosition(JKQTPKeyPosition::JKQTPKeyInsideTopLeft);
plot2->zoomToFit();
plot2->getXAxis()->setShowZeroAxis(false);
plot2->getYAxis()->setShowZeroAxis(false);
plot2->getPlotter()->setKeyPosition(JKQTPKeyPosition::JKQTPKeyInsideTopLeft);
plot3->zoomToFit();
plot3->getXAxis()->setShowZeroAxis(false);
plot3->getYAxis()->setShowZeroAxis(false);
plot3->getPlotter()->setKeyPosition(JKQTPKeyPosition::JKQTPKeyInsideTopLeft);
plot4->zoomToFit();
plot4->getXAxis()->setShowZeroAxis(false);
plot4->getYAxis()->setShowZeroAxis(false);
plot4->getPlotter()->setKeyPosition(JKQTPKeyPosition::JKQTPKeyInsideTopLeft);
plot4->setAbsoluteX(0.05, plot4->getXMax());
plot4->zoomToFit();
plot5->getXAxis()->setShowZeroAxis(false);
plot5->getYAxis()->setShowZeroAxis(false);
plot5->getPlotter()->setKeyPosition(JKQTPKeyPosition::JKQTPKeyInsideTopLeft);
plot5->setAbsoluteX(0.05, plot5->getXMax());
plot5->zoomToFit();
plot6->getXAxis()->setShowZeroAxis(false);
plot6->getYAxis()->setShowZeroAxis(false);
plot6->getPlotter()->setKeyPosition(JKQTPKeyPosition::JKQTPKeyInsideBottomRight);
plot6->zoomToFit();
// show plotter and make it a decent size
mainWidget.show();
mainWidget.resize(1600,800);
return app.exec();
}

View File

@ -0,0 +1,27 @@
# source code for this simple demo
SOURCES = jkqtplotter_simpletest_datastore_regression.cpp
# configure Qt
CONFIG += link_prl qt
QT += core gui xml svg
greaterThan(QT_MAJOR_VERSION, 4): QT += widgets printsupport
# output executable name
TARGET = jkqtplotter_simpletest_datastore_regression
# include JKQTPlotter source headers and link against library
DEPENDPATH += ../../lib ../../staticlib/jkqtplotterlib
INCLUDEPATH += ../../lib
CONFIG (debug, debug|release) {
LIBS += -L../../staticlib/jkqtplotterlib/debug -ljkqtplotterlib_debug
} else {
LIBS += -L../../staticlib/jkqtplotterlib/release -ljkqtplotterlib
}
message("LIBS = $$LIBS")
win32-msvc*: DEFINES += _USE_MATH_DEFINES
win32-msvc*: DEFINES += NOMINMAX

View File

@ -0,0 +1,8 @@
TEMPLATE = subdirs
SUBDIRS += jkqtplotterlib jkqtplotter_simpletest_datastore_regression
jkqtplotterlib.file = ../../staticlib/jkqtplotterlib/jkqtplotterlib.pro
jkqtplotter_simpletest_datastore_regression.file=$$PWD/jkqtplotter_simpletest_datastore_regression.pro
jkqtplotter_simpletest_datastore_regression.depends = jkqtplotterlib

View File

@ -0,0 +1,251 @@
# Tutorial (JKQTPDatastore): Advanced 1-Dimensional Statistics with JKQTPDatastore {#JKQTPlotterBasicJKQTPDatastoreStatistics}
[JKQTPlotterBasicJKQTPDatastore]: @ref JKQTPlotterBasicJKQTPDatastore "Basic Usage of JKQTPDatastore"
[JKQTPlotterBasicJKQTPDatastoreIterators]: @ref JKQTPlotterBasicJKQTPDatastoreIterators "Iterator-Based usage of JKQTPDatastore"
[JKQTPlotterBasicJKQTPDatastoreStatistics]: @ref JKQTPlotterBasicJKQTPDatastoreStatistics "Advanced 1-Dimensional Statistics with JKQTPDatastore"
[JKQTPlotterBasicJKQTPDatastoreRegression]: @ref JKQTPlotterBasicJKQTPDatastoreRegression "Regression Analysis (with the Statistics Library)"
[JKQTPlotterBasicJKQTPDatastoreStatisticsGroupedStat]: @ref JKQTPlotterBasicJKQTPDatastoreStatisticsGroupedStat "1-Dimensional Group Statistics with JKQTPDatastore"
[JKQTPlotterBasicJKQTPDatastoreStatistics2D]: @ref JKQTPlotterBasicJKQTPDatastoreStatistics2D "Advanced 2-Dimensional Statistics with JKQTPDatastore"
[statisticslibrary]: @ref jkqtptools_math_statistics "JKQTPlotter Statistics Library"
This tutorial project (see `./examples/simpletest_datastore_statistics/`) explains several advanced functions of JKQTPDatastore in combination with the [[statisticslibrary]] conatined in JKQTPlotter.
***Note*** that there are additional tutorial explaining other aspects of data mangement in JKQTPDatastore:
- [JKQTPlotterBasicJKQTPDatastore]
- [JKQTPlotterBasicJKQTPDatastoreIterators]
- [JKQTPlotterBasicJKQTPDatastoreStatistics]
- [JKQTPlotterBasicJKQTPDatastoreRegression]
- [JKQTPlotterBasicJKQTPDatastoreStatisticsGroupedStat]
- [JKQTPlotterBasicJKQTPDatastoreStatistics2D]
[TOC]
The source code of the main application can be found in [`jkqtplotter_simpletest_datastore_statistics.cpp`](https://github.com/jkriege2/JKQtPlotter/tree/master/examples/simpletest_datastore_statistics/jkqtplotter_simpletest_datastore_statistics.cpp).
This tutorial cites only parts of this code to demonstrate different ways of working with data for the graphs.
# Generating different sets of random numbers
The code segments below will fill four instances of JKQTPlotter with different statistical plots. All these plots are based on three sets of random numbers generated as shown here:
```.cpp
size_t randomdatacol1=datastore1->addColumn("random data 1");
size_t randomdatacol2=datastore1->addColumn("random data 2");
size_t randomdatacol3=datastore1->addColumn("random data 3");
std::random_device rd; // random number generators:
std::mt19937 gen{rd()};
std::uniform_int_distribution<> ddecide(0,1);
std::normal_distribution<> d1{0,1};
std::normal_distribution<> d2{6,1.2};
for (size_t i=0; i<150; i++) {
double v=0;
const int decide=ddecide(gen);
if (decide==0) v=d1(gen);
else v=d2(gen);
datastore1->appendToColumn(randomdatacol1, v);
if (decide==0) datastore1->appendToColumn(randomdatacol2, v);
else datastore1->appendToColumn(randomdatacol3, v);
}
```
The column `randomdatacol1` will contain 150 random numbers. Each one is drawn either from a normal dirstribution N(0,1) (`d1`) or N(6,1.2) (`d2`). the decision, which of the two to use is based on the result of a third random distribution ddecide, which only returns 0 or 1. The two columns `randomdatacol2` and `randomdatacol3` only collect the random numbers drawn from `d1` or `d2` respectively.
The three columns are generated empyt by calling `JKQTPDatastore::addColumn()` with only a name. Then the actual values are added by calling `JKQTPDatastore::appendToColumn()`.
# Basic Statistics
The three sets of random numbers from above can be visualized e.g. by a `JKQTPPeakStreamGraph` graph with code as follows:
```.cpp
JKQTPPeakStreamGraph* gData1;
plot1box->addGraph(gData1=new JKQTPPeakStreamGraph(plot1box));
gData1->setDataColumn(randomdatacol1);
gData1->setBaseline(-0.1);
gData1->setPeakHeight(-0.05);
gData1->setDrawBaseline(false);
```
This (if repeated for all three columns) results in a plot like this:
![jkqtplotter_simpletest_datastore_statistics_dataonly](https://raw.githubusercontent.com/jkriege2/JKQtPlotter/master/screenshots/jkqtplotter_simpletest_datastore_statistics_dataonly.png)
Based on the raw data we can now use JKQTPlotter's [statisticslibrary] to calculate some basic properties, like the average (`jkqtpstatAverage()`) or the standard deviation (`jkqtpstatStdDev()`):
```.cpp
size_t N=0;
double mean=jkqtpstatAverage(datastore1->begin(randomdatacol1), datastore1->end(randomdatacol1), &N);
double std=jkqtpstatStdDev(datastore1->begin(randomdatacol1), datastore1->end(randomdatacol1));
```
Both statistics functions (the same as all statistics functions in the library) use an iterator-based interface, comparable to the interface of the algorithms in the C++ standard template library. To this end, the class `JKQTPDatastore` provides an iterator interface to its columns, using the functions `JKQTPDatastore::begin()` and `JKQTPDatastore::end()`. Both functions simply receive the column ID as parameter and exist in a const and a mutable variant. the latter allows to also edit the data. In addition the function `JKQTPDatastore::backInserter()` returns a back-inserter iterator (like generated for STL containers with `std::back_inserter(container)`) that also allows to append to the column.
note that the iterator interface allows to use these functions with any container that provides such iterators (e.g. `std::vector<double>`, `std::list<int>`, `std::set<float>`, `QVector<double>`...).
The output of these functions is shown in the image above in the plot legend/key.
Of course, several other functions exist that calculate basic statistics from a column, e.g.:
- average/mean: `jkqtpstatAverage()`, `jkqtpstatWeightedAverage()`
- number of usable values in a range:`jkqtpstatCount()`
- minimum/maximum: `jkqtpstatMinMax()`, `jkqtpstatMin()`, `jkqtpstatMax()`
- sum: `jkqtpstatSum()`
- variance: `jkqtpstatVariance()`, `jkqtpstatWeightedVariance()`
- standard deviation: `jkqtpstatStdDev()`, `jkqtpstatWeightedStdDev()`
- skewnes`jkqtpstatSkewness()`
- statistical moments: `jkqtpstatCentralMoment()`, `jkqtpstatMoment()`
- correlation coefficients: `jkqtpstatCorrelationCoefficient()`
- median: `jkqtpstatMedian()`
- quantile: `jkqtpstatQuantile()`
- (N)MAD: `jkqtpstatMAD()`, `jkqtpstatNMAD()`
- 5-Number Summary (e.g. for boxplots): `jkqtpstat5NumberStatistics()`, `jkqtpstat5NumberStatisticsAndOutliers()`, `jkqtpstat5NumberStatisticsOfSortedVector()`, `jkqtpstat5NumberStatisticsAndOutliersOfSortedVector()`
All these functions use all values in the given range and convert each value to a `double`, using `jkqtp_todouble()`. The return values is always a dohble. Therefore you can use these functions to calculate statistics of ranges of any type that can be converted to `double`. Values that do not result in a valid `double`are not used in calculating the statistics. Therefore you can exclude values by setting them `JKQTP_DOUBLE_NAN` (i.e. "not a number").
# Boxplots
## Standard Boxplots
As mentioned above and shown in several other examples, JKQTPlotter supports [Boxplots](https://en.wikipedia.org/wiki/Box_plot) with the classes `JKQTPBoxplotHorizontalElement`, `JKQTPBoxplotVerticalElement`, as well as `JKQTPBoxplotHorizontal` and `JKQTPBoxplotVertical`. You can then use the 5-Number Summray functions from the [statisticslibrary] to calculate the data for such a boxplot (e.g. `jkqtpstat5NumberStatistics()`) and set it up by hand. Code would look roughly like this:
```.cpp
JKQTPStat5NumberStatistics stat=jkqtpstat5NumberStatistics(data.begin(), data.end(), 0.25, .5);
JKQTPBoxplotVerticalElement* res=new JKQTPBoxplotVerticalElement(plotter);
res->setMin(stat.minimum);
res->setMax(stat.maximum);
res->setMedian(stat.median);
res->setMean(jkqtpstatAverage(first, last));
res->setPercentile25(stat.quantile1);
res->setPercentile75(stat.quantile2);
res->setMedianConfidenceIntervalWidth(stat.IQRSignificanceEstimate());
res->setDrawMean(true);
res->setDrawNotch(true);
res->setDrawMedian(true);
res->setDrawMinMax(true);
res->setDrawBox(true);
res->setPos(boxposX);
plotter->addGraph(res);
```
In order to save you the work of writing out this code, the [statisticslibrary] provides "adaptors", such as `jkqtpstatAddVBoxplot()`, which basically implements the code above. Then drawing a boxplot is reduced to:
```.cpp
JKQTPBoxplotHorizontalElement* gBox2=jkqtpstatAddHBoxplot(plot1box->getPlotter(), datastore1->begin(randomdatacol2), datastore1->end(randomdatacol2), -0.25);
gBox2->setColor(gData2->getKeyLabelColor());
gBox2->setBoxWidthAbsolute(16);
```
Here `-0.25`indicates the location (on the y-axis) of the boxplot. and the plot is calculated for the data in the `JKQTPDatastore` column `randomdatacol2`.
![jkqtplotter_simpletest_datastore_statistics_boxplots_simple](https://raw.githubusercontent.com/jkriege2/JKQtPlotter/master/screenshots/jkqtplotter_simpletest_datastore_statistics_boxplots_simple.png)
## Boxplots with Outliers
Usually the boxplot draws its whiskers at the minimum and maximum value of the dataset. But if your data contains a lot of outliers, it may make sense to draw them e.g. at the 3% and 97% quantiles and the draw the outliers as additional data points. This can also be done with `jkqtpstat5NumberStatistics()`, as you can specify the minimum and maximum quantile (default is 0 and 1, i.e. the true minimum and maximum) and the resulting object contains a vector with the outlier values. Then you could add them to the JKQTPDatastore and add a scatter plot that displays them. Also this task is sped up by an "adaptor". Simply call
```.cpp
std::pair<JKQTPBoxplotHorizontalElement*,JKQTPSingleColumnSymbolsGraph*> gBox1;
gBox1=jkqtpstatAddHBoxplotAndOutliers(plot1box->getPlotter(), datastore1->begin(randomdatacol1), datastore1->end(randomdatacol1), -0.3,
0.25, 0.75, // 1. and 3. Quartile for the boxplot box
0.03, 0.97 // Quantiles for the boxplot box whiskers' ends
);
```
As you can see this restuns the `JKQTPBoxplotHorizontalElement` and in addition a `JKQTPSingleColumnSymbolsGraph` for the display of the outliers. The result looks like this:
![jkqtplotter_simpletest_datastore_statistics_boxplots_outliers](https://raw.githubusercontent.com/jkriege2/JKQtPlotter/master/screenshots/jkqtplotter_simpletest_datastore_statistics_boxplots_outliers.png)
# Histograms
Calculating 1D-Histograms is supported by several functions from the [statisticslibrary], e.g. `jkqtpstatHistogram1DAutoranged()`. You can use the result to fill new columns in a `JKQTPDatastore`, which can then be used to draw the histogram (here wit 15 bins, spanning the full data range):
```.cpp
size_t histcolX=plotter->getDatastore()->addColumn(histogramcolumnBaseName+", bins");
size_t histcolY=plotter->getDatastore()->addColumn(histogramcolumnBaseName+", values");
jkqtpstatHistogram1DAutoranged(first, last, plotter->getDatastore()->backInserter(histcolX), plotter->getDatastore()->backInserter(histcolY), 15);
JKQTPBarVerticalGraph* resO=new JKQTPBarVerticalGraph(plotter);
resO->setXColumn(histcolX);
resO->setYColumn(histcolY);
resO->setTitle(histogramcolumnBaseName);
plotter->addGraph(resO);
```
Again there are "adaptors" which significanty reduce the amount of coude you have to type:
```.cpp
JKQTPBarVerticalGraph* hist1=jkqtpstatAddHHistogram1DAutoranged(plot1->getPlotter(), datastore1->begin(randomdatacol1), datastore1->end(randomdatacol1), 15);
```
The resulting plot looks like this (the distributions used to generate the random data are also shown as line plots!):
![jkqtplotter_simpletest_datastore_statistics_hist](https://raw.githubusercontent.com/jkriege2/JKQtPlotter/master/screenshots/jkqtplotter_simpletest_datastore_statistics_hist.png)
# Kernel Density Estimates (KDE)
Especially when only few samples from a distribution are available, histograms are not good at representing the underlying data distribution. In such cases, [Kernel Density Estimates (KDE)](https://en.wikipedia.org/wiki/Kernel_density_estimation) can help, which are basically a smoothed variant of a histogram. The [statisticslibrary] supports calculating them via e.g. `jkqtpstatKDE1D()`:
```.cpp
size_t kdecolX=plotter->getDatastore()->addColumn(KDEcolumnBaseName+", bins");
size_t kdecolY=plotter->getDatastore()->addColumn(KDEcolumnBaseName+", values");
jkqtpstatKDE1D(first, last, -5.0,0.01,10.0, plotter->getDatastore()->backInserter(kdecolX), plotter->getDatastore()->backInserter(kdecolY), kernel, kdeBandwidth);
JKQTPXYLineGraph* resO=new JKQTPXYLineGraph(plotter);
resO->setXColumn(kdecolX);
resO->setYColumn(kdecolY);
resO->setTitle(KDEcolumnBaseName);
resO->setDrawLine(true);
resO->setSymbolType(JKQTPNoSymbol);
plotter->addGraph(resO);
```
The function accepts different kernel functions (any C++ functor `double f(double x)`) and provides a set of default kernels, e.g.
- `jkqtpstatKernel1DEpanechnikov()`
- `jkqtpstatKernel1DGaussian()`
- `jkqtpstatKernel1DUniform()`
- ...
The three parameters `-5.0, 0.01, 10.0` tell the function `jkqtpstatKDE1D()` to evaluate the KDE at positions between -5 and 10, in steps of 0.01.
Finally the bandwidth constrols the smoothing and the [statisticslibrary] provides a simple function to estimate it automatically from the data:
```.cpp
double kdeBandwidth=jkqtpstatEstimateKDEBandwidth(datastore1->begin(randomdatacol1subset), datastore1->end(randomdatacol1subset));
```
Again a shortcut "adaptor" simplifies this task:
```.cpp
JKQTPXYLineGraph* kde2=jkqtpstatAddHKDE1D(plot1kde->getPlotter(), datastore1->begin(randomdatacol1subset), datastore1->end(randomdatacol1subset),
// evaluate at locations between -5 and 10, in steps of 0.01 (equivalent to the line above, but without pre-calculating a vector)
-5.0,0.01,10.0,
// use a gaussian kernel
&jkqtpstatKernel1DEpanechnikov,
// estimate the bandwidth
kdeBandwidth);
```
Plots that result from such calls look like this:
![jkqtplotter_simpletest_datastore_statistics_kde](https://raw.githubusercontent.com/jkriege2/JKQtPlotter/master/screenshots/jkqtplotter_simpletest_datastore_statistics_kde.png)
# Cummulative Histograms and KDEs
Both histograms and KDEs support a parameter `bool cummulative`, which allows to accumulate the data after calculation and drawing cummulative histograms/KDEs:
```.cpp
JKQTPBarVerticalGraph* histcum2=jkqtpstatAddHHistogram1DAutoranged(plot1cum->getPlotter(), datastore1->begin(randomdatacol2), datastore1->end(randomdatacol2),
// bin width
0.1,
// normalized, cummulative
false, true);
```
![jkqtplotter_simpletest_datastore_statistics_cumhistkde](https://raw.githubusercontent.com/jkriege2/JKQtPlotter/master/screenshots/jkqtplotter_simpletest_datastore_statistics_cumhistkde.png)
# Screenshot of the full Program
The output of the full test program [`jkqtplotter_simpletest_datastore_statistics.cpp`](https://github.com/jkriege2/JKQtPlotter/tree/master/examples/simpletest_datastore_statistics/jkqtplotter_simpletest_datastore_statistics.cpp) looks like this:
![jkqtplotter_simpletest_datastore_statistics](https://raw.githubusercontent.com/jkriege2/JKQtPlotter/master/screenshots/jkqtplotter_simpletest_datastore_statistics.png)

View File

@ -0,0 +1,289 @@
/** \example jkqtplotter_simpletest_datastore_statistics.cpp
* Explains how to use the internal statistics library (see \ref jkqtptools_statistics ) together with JKQTPDatastore to generate advanced plots for 1-dimensional data.
*
* \ref JKQTPlotterBasicJKQTPDatastoreStatistics
*/
#include <QApplication>
#include "jkqtplotter/jkqtplotter.h"
#include "jkqtplotter/jkqtpgraphspeakstream.h"
#include "jkqtplotter/jkqtpgraphsboxplot.h"
#include "jkqtplotter/jkqtpgraphsstatisticsadaptors.h"
#include "jkqtplotter/jkqtpgraphsevaluatedfunction.h"
#include "jkqtcommon/jkqtpstatisticstools.h"
#include "jkqtcommon/jkqtpstringtools.h"
#include <random>
#include <cmath>
int main(int argc, char* argv[])
{
QApplication app(argc, argv);
// 1. create a window with several plotters and get a pointer to the internal datastores (for convenience)
QWidget mainWidget;
QGridLayout* lay;
mainWidget.setLayout(lay=new QGridLayout);
JKQTPlotter* plot1=new JKQTPlotter(&mainWidget);
plot1->getPlotter()->setPlotLabel("Histograms and KDE");
JKQTPDatastore* datastore1=plot1->getDatastore();
lay->addWidget(plot1,1,0);
JKQTPlotter* plot1cum=new JKQTPlotter(datastore1, &mainWidget);
plot1cum->getPlotter()->setPlotLabel("Cummulative Histogram");
lay->addWidget(plot1cum,1,1);
JKQTPlotter* plot1kde=new JKQTPlotter(datastore1, &mainWidget);
plot1kde->getPlotter()->setPlotLabel("Kernel Density Estimate");
lay->addWidget(plot1kde,0,1);
JKQTPlotter* plot1box=new JKQTPlotter(datastore1, &mainWidget);
plot1box->getPlotter()->setPlotLabel("Boxplots");
lay->addWidget(plot1box,0,0);
// 2. Now we create two vectors with random values
// vector 1: The values are drawn from two different normal distributions d1 and d2,
// where for each datapoint the distribution is chosen randomly (by ddecide)
// vector 2: same values as in vector 1, if the value is drawn from d1
// vector 3: same values as in vector 1, if the value is drawn from d2
size_t randomdatacol1=datastore1->addColumn("random data 1");
size_t randomdatacol2=datastore1->addColumn("random data 2");
size_t randomdatacol3=datastore1->addColumn("random data 3");
std::random_device rd; // random number generators:
std::mt19937 gen{rd()};
std::uniform_int_distribution<> ddecide(0,1);
std::normal_distribution<> d1{0,1};
std::normal_distribution<> d2{6,1.2};
for (size_t i=0; i<150; i++) {
double v=0;
const int decide=ddecide(gen);
if (decide==0) v=d1(gen);
else v=d2(gen);
datastore1->appendToColumn(randomdatacol1, v);
if (decide==0) datastore1->appendToColumn(randomdatacol2, v);
else datastore1->appendToColumn(randomdatacol3, v);
}
QString d1_latex="$\\mathcal{N}("+jkqtp_floattolatexqstr(d1.mean(), 1)+","+jkqtp_floattolatexqstr(d1.stddev(), 1)+")$";
QString d2_latex="$\\mathcal{N}("+jkqtp_floattolatexqstr(d2.mean(), 1)+","+jkqtp_floattolatexqstr(d2.stddev(), 1)+")$";
// 3.1. To visualize the data, a simple JKQTPPeakStreamGraph is used:
JKQTPPeakStreamGraph* gData1;
plot1box->addGraph(gData1=new JKQTPPeakStreamGraph(plot1box));
gData1->setDataColumn(randomdatacol1);
gData1->setBaseline(-0.1);
gData1->setPeakHeight(-0.05);
gData1->setDrawBaseline(false);
// 3.2. We calculate some basic statistics of that column and display it in the graph legend (via the graph title):
// Here we use functions of the statistics library for the first time. The statistics library uses an iterator interface
// scheme, much like the algorithms of the C++ standard library. Therefore we the iterator interface of JKQTPDatastore
// when calling the statistics functions.
size_t N=0;
double mean=jkqtpstatAverage(datastore1->begin(randomdatacol1), datastore1->end(randomdatacol1), &N);
double std=jkqtpstatStdDev(datastore1->begin(randomdatacol1), datastore1->end(randomdatacol1));
gData1->setTitle(QString("random data $"+d1_latex+"+"+d2_latex+"$: $\\overline{X_1}=%1, \\sigma_{X_1}=%2, N_{X_3}=%3$").arg(jkqtp_floattolatexqstr(mean, 2)).arg(jkqtp_floattolatexqstr(std, 2)).arg(N));
// 3.3. same as 3.1-3.2, but for the second and thirdcolumn of data:
JKQTPPeakStreamGraph* gData2;
plot1box->addGraph(gData2=new JKQTPPeakStreamGraph(plot1box));
gData2->setDataColumn(randomdatacol2);
gData2->setBaseline(-0.1);
gData2->setPeakHeight(0.05);
gData2->setDrawBaseline(false);
N=0;
mean=jkqtpstatAverage(datastore1->begin(randomdatacol2), datastore1->end(randomdatacol2), &N);
std=jkqtpstatStdDev(datastore1->begin(randomdatacol2), datastore1->end(randomdatacol2));
gData2->setTitle(QString("random data subset $"+d1_latex+"$: $\\overline{X_2}=%1, \\sigma_{X_3}=%2, N_{X_3}=%3$").arg(jkqtp_floattolatexqstr(mean, 2)).arg(jkqtp_floattolatexqstr(std, 2)).arg(N));
JKQTPPeakStreamGraph* gData3;
plot1box->addGraph(gData3=new JKQTPPeakStreamGraph(plot1box));
gData3->setDataColumn(randomdatacol3);
gData3->setBaseline(-0.15);
gData3->setPeakHeight(-0.05);
gData3->setDrawBaseline(false);
N=0;
mean=jkqtpstatAverage(datastore1->begin(randomdatacol3), datastore1->end(randomdatacol3), &N);
std=jkqtpstatStdDev(datastore1->begin(randomdatacol3), datastore1->end(randomdatacol3));
gData3->setTitle(QString("random data subset $"+d2_latex+"$: $\\overline{X_3}=%1, \\sigma_{X_3}=%2, N_{X_3}=%3$").arg(jkqtp_floattolatexqstr(mean, 2)).arg(jkqtp_floattolatexqstr(std, 2)).arg(N));
// 3.4. Now we calculate a 5-Value Summary of the two datasets and use it to plot corresponding boxplots
// This can be done by hand, or you can call jkqtpstatAddHBoxplot() which saves some typing. This function
// uses jkqtpstat5NumberStatistics() internally to calculate the statistics.
JKQTPBoxplotHorizontalElement* gBox2=jkqtpstatAddHBoxplot(plot1box->getPlotter(), datastore1->begin(randomdatacol2), datastore1->end(randomdatacol2), -0.25);
gBox2->setColor(gData2->getKeyLabelColor());
gBox2->setBoxWidthAbsolute(16);
JKQTPBoxplotHorizontalElement* gBox3=jkqtpstatAddHBoxplot(plot1box->getPlotter(), datastore1->begin(randomdatacol3), datastore1->end(randomdatacol3), -0.35);
gBox3->setColor(gData3->getKeyLabelColor());
gBox3->setBoxWidthAbsolute(16);
// 3.5. In addition to jkqtpstatAddHBoxplot() there is also jkqtpstatAddHBoxplotAndOutliers(), which generates two graphs:
// one JKQTPBoxplotHorizontalElement for the boxplot and one JKQTPSingleColumnSymbolsGraph for the outliers
// Note that this function generates additional data columns in the datastore of the provided plotter to represent
// the outlier locations.
// jkqtpstatAddHBoxplotAndOutliers() calculates the 3% and 97% Quantiles for the boxplots whiskers' ends. You can change that
// by supplying other quantiles to the call
std::pair<JKQTPBoxplotHorizontalElement*,JKQTPSingleColumnSymbolsGraph*> gBox1=jkqtpstatAddHBoxplotAndOutliers(plot1box->getPlotter(), datastore1->begin(randomdatacol1), datastore1->end(randomdatacol1), -0.3);
// jkqtpstatAddHBoxplotAndOutliers() calculates the 3% and 97% Quantiles for the boxplots whiskers' ends. You can change that
// by supplying other quantiles to the call
//std::pair<JKQTPBoxplotHorizontalElement*,JKQTPXYLineGraph*> gBox1=jkqtpstatAddHBoxplotAndOutliers(plot1box->getPlotter(), datastore1->begin(randomdatacol1), datastore1->end(randomdatacol1), -0.3,
// 0.25, 0.75, // 1. and 3. Quartile for the boxplot box
// 0.05, 0.95 // Quantiles for the boxplot box whiskers' ends
// );
gBox1.first->setColor(gData1->getKeyLabelColor());
gBox1.second->setColor(gData1->getKeyLabelColor());
gBox1.second->setSymbolType(JKQTPGraphSymbols::JKQTPCircle);
gBox1.second->setSymbolSize(7);
gBox1.first->setBoxWidthAbsolute(16);
// the simple alternative would have been:
//JKQTPBoxplotHorizontalElement* gBox1;
//plot1box->addGraph(gBox1=jkqtpstatAddHBoxplot(plot1box->getPlotter(), datastore1->begin(randomdatacol1), datastore1->end(randomdatacol1)));
//gBox1->setPos(-0.3);
//gBox1->setColor(gData1->getKeyLabelColor());
//gBox1->setBoxWidthAbsolute(16);
// 4.1. We repeat the JKQTPPeakStreamGraph visualization from above:
plot1->addGraph(gData1=new JKQTPPeakStreamGraph(plot1));
gData1->setDataColumn(randomdatacol1);
gData1->setBaseline(-0.1);
gData1->setPeakHeight(-0.05);
gData1->setDrawBaseline(false);
gData1->setTitle("random data $"+d1_latex+"+"+d2_latex+"$");
// 4.2. same as 3.1-3.2, but for the second and thirdcolumn of data:
plot1->addGraph(gData2=new JKQTPPeakStreamGraph(plot1));
gData2->setDataColumn(randomdatacol2);
gData2->setBaseline(-0.1);
gData2->setPeakHeight(0.05);
gData2->setDrawBaseline(false);
gData2->setTitle("random data subset $"+d1_latex+"$");
plot1->addGraph(gData3=new JKQTPPeakStreamGraph(plot1));
gData3->setDataColumn(randomdatacol3);
gData3->setBaseline(-0.15);
gData3->setPeakHeight(-0.05);
gData3->setDrawBaseline(false);
gData3->setTitle("random data subset $"+d2_latex+"$");
// 4.3. for comparison we add plots of the initial distributions:
plot1->addGraph(new JKQTPXFunctionLineGraph(std::bind(&jkqtp_gaussdist, std::placeholders::_1, d1.mean(), d1.stddev()), d1_latex, plot1));
plot1->addGraph(new JKQTPXFunctionLineGraph(std::bind(&jkqtp_gaussdist, std::placeholders::_1, d2.mean(), d2.stddev()), d2_latex, plot1));
// 4.1. next we calculate a histogram of the data and add a plot to the graph:
JKQTPBarVerticalGraph* hist1=jkqtpstatAddHHistogram1DAutoranged(plot1->getPlotter(), datastore1->begin(randomdatacol1), datastore1->end(randomdatacol1), 15);
// here the bins are defined by the full range of the data and the bin count (15) is specified
// alternatively you could specify the bin width and the number would be calculated automatically:
//JKQTPBarVerticalGraph* hist1=jkqtpstatAddHHistogram1DAutoranged(plot1->getPlotter(), datastore1->begin(randomdatacol1), datastore1->end(randomdatacol1), 0.5);
// a third option is to define the bins via a vector of values (lower borders):
//std::vector<double> bins{-2,-1.5,-1,-0.75,-0.5,-0.25,0,0.25,0.5,0.75,1,1.5,2,2.5,3,4,5,6,7,8,9,10};
//JKQTPBarVerticalGraph* hist1=jkqtpstatAddHHistogram1D(plot1->getPlotter(), datastore1->begin(randomdatacol1), datastore1->end(randomdatacol1), bins.begin(), bins.end());
hist1->setColor(QColorWithAlphaF(gData1->getKeyLabelColor(), 0.5)); // use same color as gData1, but with alpha set to 0.5 (50% transparency)
// 5.1. instead of histograms, it can also make sense to calculate Kernel Density Estimates, especially when only few datapoints are available.
// To demonstrate this, we first calculate take a subset of the values in randomdatacol1 as a small test dataset.
size_t randomdatacol1subset=datastore1->copyColumn(randomdatacol1, 1, 7, "subset of "+datastore1->getColumnName(randomdatacol1));
JKQTPPeakStreamGraph* gData2kde;
plot1kde->addGraph(gData2kde=new JKQTPPeakStreamGraph(plot1kde));
gData2kde->setDataColumn(randomdatacol1subset);
gData2kde->setBaseline(-0.05);
gData2kde->setPeakHeight(-0.1);
gData2kde->setDrawBaseline(false);
gData2kde->setTitle("data");
// first we plot the histogram of this dataset, with 0.5 bin width:
JKQTPBarVerticalGraph* hist1kde=jkqtpstatAddHHistogram1DAutoranged(plot1kde->getPlotter(), datastore1->begin(randomdatacol1subset), datastore1->end(randomdatacol1subset), 0.5);
hist1kde->setTitle("histogram");
hist1kde->setColor(QColorWithAlphaF(gData2kde->getKeyLabelColor(), 0.25)); // use same color as gData1, but with alpha set to 0.5 (50% transparency)
// 5.2. now we first extimate the bandwidth:
double kdeBandwidth=jkqtpstatEstimateKDEBandwidth(datastore1->begin(randomdatacol1subset), datastore1->end(randomdatacol1subset));
// and generate a vector of positions, where we want to evaluate the KDE:
std::vector<double> xKDE;
for (double x=-5; x<=10; x+=0.01) xKDE.push_back(x);
// now the KDE can be added (gaussian kernel, evaluated at the positions in xKDE):
JKQTPXYLineGraph* kde1=jkqtpstatAddHKDE1D(plot1kde->getPlotter(), datastore1->begin(randomdatacol1subset), datastore1->end(randomdatacol1subset),
// evaluate at locations in xKDE
xKDE.begin(), xKDE.end(),
// use a gaussian kernel
&jkqtpstatKernel1DGaussian,
// estimate the bandwidth
kdeBandwidth);
kde1->setTitle("KDE, gaussian, $\\mbox{BW}="+jkqtp_floattolatexqstr(kdeBandwidth, 3)+"$");
JKQTPXYLineGraph* kde11=jkqtpstatAddHKDE1D(plot1kde->getPlotter(), datastore1->begin(randomdatacol1subset), datastore1->end(randomdatacol1subset),
// evaluate at locations in xKDE
xKDE.begin(), xKDE.end(),
// use a gaussian kernel
&jkqtpstatKernel1DGaussian,
// a very small bandwidth
0.1);
kde11->setTitle("KDE, gaussian, $\\mbox{BW}="+jkqtp_floattolatexqstr(0.1, 3)+"$");
// here a second KDE with a different kernel (Epanechnikov) and the range of evaluation positions defined via three numbers:
JKQTPXYLineGraph* kde2=jkqtpstatAddHKDE1D(plot1kde->getPlotter(), datastore1->begin(randomdatacol1subset), datastore1->end(randomdatacol1subset),
// evaluate at locations between -5 and 10, in steps of 0.01 (equivalent to the line above, but without pre-calculating a vector)
-5.0,0.01,10.0,
// use a gaussian kernel
&jkqtpstatKernel1DEpanechnikov,
// estimate the bandwidth
kdeBandwidth);
kde2->setTitle("KDE, epanechnikov, $\\mbox{BW}="+jkqtp_floattolatexqstr(kdeBandwidth, 3)+"$");
kde1->setColor(QColorWithAlphaF(gData2kde->getKeyLabelColor(), 0.5)); // use same color as gData1, but with alpha set to 0.5 (50% transparency)
// 5.3. for comparison we add plots of the initial distributions:
plot1kde->addGraph(new JKQTPXFunctionLineGraph(std::bind(&jkqtp_gaussdist, std::placeholders::_1, d1.mean(), d1.stddev()), d1_latex, plot1));
plot1kde->addGraph(new JKQTPXFunctionLineGraph(std::bind(&jkqtp_gaussdist, std::placeholders::_1, d2.mean(), d2.stddev()), d2_latex, plot1));
// 6.1. now we calculate a cummulative histogram:
JKQTPPeakStreamGraph* gData2com;
plot1cum->addGraph(gData2com=new JKQTPPeakStreamGraph(plot1cum));
gData2com->setDataColumn(randomdatacol2);
gData2com->setBaseline(-1);
gData2com->setPeakHeight(-10);
gData2com->setDrawBaseline(false);
JKQTPBarVerticalGraph* histcum2=jkqtpstatAddHHistogram1DAutoranged(plot1cum->getPlotter(), datastore1->begin(randomdatacol2), datastore1->end(randomdatacol2),
// bin width
0.1,
// normalized, cummulative
false, true);
histcum2->setColor(QColorWithAlphaF(gData2com->getKeyLabelColor(), 0.2)); // use same color as gData1, but with alpha set to 0.5 (50% transparency)
// 6.2. also a kernel density estimate can be accumulated:
JKQTPXYLineGraph* kdecum2=jkqtpstatAddHKDE1D(plot1cum->getPlotter(), datastore1->begin(randomdatacol2), datastore1->end(randomdatacol2),
// evaluate at locations between -3.5 and 3.5, in steps of 0.01
-3.5,0.01,3.5,
// use a uniform/box kernel
&jkqtpstatKernel1DUniform,
// estimate the bandwidth
jkqtpstatEstimateKDEBandwidth(datastore1->begin(randomdatacol2), datastore1->end(randomdatacol2)),
// cummulative KDE:
true);
kdecum2->setColor(gData2com->getKeyLabelColor()); // use same color as gData1, but with alpha set to 0.5 (50% transparency)
// autoscale the plot so the graph is contained
plot1->zoomToFit();
plot1->setGrid(false);
plot1->getXAxis()->setShowZeroAxis(false);
plot1->getPlotter()->setKeyBackgroundColor(QColorWithAlphaF("white", 0.25), Qt::SolidPattern);
plot1->setY(-0.25, 0.45);
plot1cum->zoomToFit();
plot1cum->setGrid(false);
plot1cum->getXAxis()->setShowZeroAxis(false);
plot1cum->getPlotter()->setKeyBackgroundColor(QColorWithAlphaF("white", 0.25), Qt::SolidPattern);
plot1kde->zoomToFit();
plot1kde->setGrid(false);
plot1kde->getXAxis()->setShowZeroAxis(false);
plot1kde->getPlotter()->setKeyBackgroundColor(QColorWithAlphaF("white", 0.25), Qt::SolidPattern);
plot1kde->setY(-0.155, 0.45);
plot1box->zoomToFit();
plot1box->setGrid(false);
plot1box->getXAxis()->setShowZeroAxis(false);
plot1box->getPlotter()->setKeyBackgroundColor(QColorWithAlphaF("white", 0.25), Qt::SolidPattern);
plot1box->setY(-0.4, 0.0);
// show plotter and make it a decent size
mainWidget.show();
mainWidget.resize(1200,800);
return app.exec();
}

View File

@ -0,0 +1,27 @@
# source code for this simple demo
SOURCES = jkqtplotter_simpletest_datastore_statistics.cpp
# configure Qt
CONFIG += link_prl qt
QT += core gui xml svg
greaterThan(QT_MAJOR_VERSION, 4): QT += widgets printsupport
# output executable name
TARGET = jkqtplotter_simpletest_datastore_statistics
# include JKQTPlotter source headers and link against library
DEPENDPATH += ../../lib ../../staticlib/jkqtplotterlib
INCLUDEPATH += ../../lib
CONFIG (debug, debug|release) {
LIBS += -L../../staticlib/jkqtplotterlib/debug -ljkqtplotterlib_debug
} else {
LIBS += -L../../staticlib/jkqtplotterlib/release -ljkqtplotterlib
}
message("LIBS = $$LIBS")
win32-msvc*: DEFINES += _USE_MATH_DEFINES
win32-msvc*: DEFINES += NOMINMAX

View File

@ -0,0 +1,8 @@
TEMPLATE = subdirs
SUBDIRS += jkqtplotterlib jkqtplotter_simpletest_datastore_statistics
jkqtplotterlib.file = ../../staticlib/jkqtplotterlib/jkqtplotterlib.pro
jkqtplotter_simpletest_datastore_statistics.file=$$PWD/jkqtplotter_simpletest_datastore_statistics.pro
jkqtplotter_simpletest_datastore_statistics.depends = jkqtplotterlib

View File

@ -0,0 +1,287 @@
# Tutorial (JKQTPDatastore): Advanced 2-Dimensional Statistics with JKQTPDatastore {#JKQTPlotterBasicJKQTPDatastoreStatistics2D}
[JKQTPlotterBasicJKQTPDatastore]: @ref JKQTPlotterBasicJKQTPDatastore "Basic Usage of JKQTPDatastore"
[JKQTPlotterBasicJKQTPDatastoreIterators]: @ref JKQTPlotterBasicJKQTPDatastoreIterators "Iterator-Based usage of JKQTPDatastore"
[JKQTPlotterBasicJKQTPDatastoreStatistics]: @ref JKQTPlotterBasicJKQTPDatastoreStatistics "Advanced 1-Dimensional Statistics with JKQTPDatastore"
[JKQTPlotterBasicJKQTPDatastoreRegression]: @ref JKQTPlotterBasicJKQTPDatastoreRegression "Regression Analysis (with the Statistics Library)"
[JKQTPlotterBasicJKQTPDatastoreStatisticsGroupedStat]: @ref JKQTPlotterBasicJKQTPDatastoreStatisticsGroupedStat "1-Dimensional Group Statistics with JKQTPDatastore"
[JKQTPlotterBasicJKQTPDatastoreStatistics2D]: @ref JKQTPlotterBasicJKQTPDatastoreStatistics2D "Advanced 2-Dimensional Statistics with JKQTPDatastore"
[statisticslibrary]: @ref jkqtptools_math_statistics "JKQTPlotter Statistics Library"
This tutorial project (see `./examples/simpletest_datastore_statistics_2d/`) explains several advanced functions of JKQTPDatastore in combination with the [[statisticslibrary]] conatined in JKQTPlotter.
***Note*** that there are additional tutorial explaining other aspects of data mangement in JKQTPDatastore:
- [JKQTPlotterBasicJKQTPDatastore]
- [JKQTPlotterBasicJKQTPDatastoreIterators]
- [JKQTPlotterBasicJKQTPDatastoreStatistics]
- [JKQTPlotterBasicJKQTPDatastoreRegression]
- [JKQTPlotterBasicJKQTPDatastoreStatisticsGroupedStat]
- [JKQTPlotterBasicJKQTPDatastoreStatistics2D]
[TOC]
The source code of the main application can be found in [`jkqtplotter_simpletest_datastore_statistics_2d.cpp`](https://github.com/jkriege2/JKQtPlotter/tree/master/examples/simpletest_datastore_statistics_2d/jkqtplotter_simpletest_datastore_statistics_2d.cpp).
This tutorial cites only parts of this code to demonstrate different ways of working with data for the graphs.
# Generating different sets of random numbers
The code segments below will fill different instances of JKQTPlotter with different statistical plots. All these plots are based on either of two sets of random number pairs (i.e. x/y-locations) generated as shown here:
```.cpp
size_t randomdatacolx_small=datastore1->addColumn("random data, x");
size_t randomdatacoly_small=datastore1->addColumn("random data, y");
size_t randomdatacoldist_small=datastore1->addColumn("random data, distribution/class");
size_t randomdatacolx=datastore1->addColumn("random data, x");
size_t randomdatacoly=datastore1->addColumn("random data, y");
size_t randomdatacoldist=datastore1->addColumn("random data, distribution/class");
// random number generators:
std::random_device rd;
std::mt19937 gen{rd()};
std::uniform_int_distribution<> ddecide(0,2);
std::normal_distribution<> d1x{5,3};
std::normal_distribution<> d1y{5,1};
std::normal_distribution<> d2x{10,2};
std::normal_distribution<> d2y{10,5};
for (size_t i=0; i<500; i++) {
double rx=0,ry=0;
const int decide=ddecide(gen);
if (decide==0) {
rx=d1x(gen);
ry=d1y(gen);
} else {
rx=d2x(gen);
ry=d2y(gen);
}
if (i<150) {
datastore1->appendToColumn(randomdatacolx_small, rx);
datastore1->appendToColumn(randomdatacoly_small, ry);
datastore1->appendToColumn(randomdatacoldist_small, std::min(1,decide));
}
datastore1->appendToColumn(randomdatacolx, rx);
datastore1->appendToColumn(randomdatacoly, ry);
datastore1->appendToColumn(randomdatacoldist, std::min(1,decide));
}
```
The columns `randomdatacolx` and `randomdatacoly` will contain 500 random numbers. Each one is drawn either from the normal distributions `d1x`/`d1y`, or `d2x`/`d2y`. The decision, which of the two to use is based on the result of a third random distribution `ddecide`, which only returns 0, 1 or 2 (`d1x`/`d1y` is chosen for `ddecide==0`). The column `randomdatacoldist` finally encodes which pair of distributions was chosen. The three columns `randomdatacolx_small`, `randomdatacoly_small` and `randomdatacoldist_small` represent a subset of the first 150 numbers from the full dataset.
The columns are generated empty by calling `JKQTPDatastore::addColumn()` with only a name. Then the actual values are added by calling `JKQTPDatastore::appendToColumn()`.
The datasets generated above can be plotted using a simple scatterplot (here shown for the full dataset only):
```.cpp
JKQTPXYParametrizedScatterGraph* gDataHist;
plothist->addGraph(gDataHist=new JKQTPXYParametrizedScatterGraph(plothist));
gDataHist->setXYColumns(randomdatacolx,randomdatacoly);
gDataHist->setSymbolColumn(randomdatacoldist);
QMap<double, JKQTPGraphSymbols> mapped;
mapped[0]=JKQTPGraphSymbols::JKQTPCross;
mapped[1]=JKQTPGraphSymbols::JKQTPPlus;
gDataHist->setMappedSymbolColumnFunctor(mapped);
gDataHist->setSymbolSize(5);
gDataHist->setSymbolColor(QColorWithAlphaF(QColor("red"), 0.7));
gDataHist->setDrawLine(false);
gDataHist->setTitle(QString("random data, $N="+QString::number(datastore1->getRows(randomdatacoldist))+"$"));
```
In addition to get an idea of the used distributions, we can add two ellipses indicating the (doubled) standard deviations of the two distributions:
```.cpp
// 2.3. to visualize the initial distributions, we draw an ellipse indicating the
// variance of the distributions
JKQTPGeoEllipse* gEll1Hist;
JKQTPGeoEllipse* gEll2Hist;
plothist->addGraph(gEll1Hist=new JKQTPGeoEllipse(plothist, d1x.mean(), d1y.mean(),d1x.stddev()*2.0,d1y.stddev()*2.0));
plothist->addGraph(gEll2Hist=new JKQTPGeoEllipse(plothist, d2x.mean(), d2y.mean(),d2x.stddev()*2.0,d2y.stddev()*2.0));
```
The resulting plot looks like this:
![jkqtplotter_simpletest_datastore_statistics_2d_scatteronly](https://raw.githubusercontent.com/jkriege2/JKQtPlotter/master/screenshots/jkqtplotter_simpletest_datastore_statistics_2d_scatteronly.png)
# Marginal Statistics
To explore the statistical properties of a 2D dataset, it is often useful to draw the marginal histograms, i.e. the histograms of one of the two variables. To do so, we use the function `jkqtpstatAddHHistogram1DAutoranged()` and `jkqtpstatAddVHistogram1DAutoranged()` that were introduced in [JKQTPlotterBasicJKQTPDatastoreStatistics] and are explained in detail there:
```.cpp
jkqtpstatAddHHistogram1DAutoranged(plothistBottom->getPlotter(), datastore1->begin(randomdatacolx), datastore1->end(randomdatacolx), 1.0, true);
jkqtpstatAddVHistogram1DAutoranged(plothistLeft->getPlotter(), datastore1->begin(randomdatacoly), datastore1->end(randomdatacoly), 1.0, true);
```
Note that the plots are put into `JKQTPlotter`-instances `plothistBottom` and `plothistLeft`. These are put into a global `QGridSizer` just below and left of the main histogram plot `plothist`. The axes of these plots are linked to the main plot, so zooming one affects the other plot too and their axes are inverted, so the drawn histogram elongates to the left and to the bottom (starting with 0 at the main plot `plothist`):
```.cpp
QGridLayout* lay;
JKQTPlotter* plothist=new JKQTPlotter(&mainWidget);
lay->addWidget(plothist,0,1);
plothist->getPlotter()->setPlotLabel("Histograms");
JKQTPDatastore* datastore1=plothist->getDatastore();
JKQTPlotter* plothistLeft=new JKQTPlotter(datastore1, &mainWidget);
lay->addWidget(plothistLeft,0,0);
JKQTPlotter* plothistBottom=new JKQTPlotter(datastore1, &mainWidget);
lay->addWidget(plothistBottom,1,1);
plothistLeft->synchronizeYToMaster(plothist);
plothistLeft->setAbsoluteX(0,1);
plothistLeft->getXAxis()->setInverted(true);
plothistLeft->getXAxis()->setShowZeroAxis(false);
plothistLeft->getYAxis()->setShowZeroAxis(false);
plothistLeft->getYAxis()->setDrawMode1(JKQTPCADMLine);
plothistLeft->getYAxis()->setDrawMode2(JKQTPCADMLineTicks);
plothistLeft->setGrid(false);
plothistBottom->synchronizeXToMaster(plothist);
plothistBottom->setAbsoluteY(0,1);
plothistBottom->getYAxis()->setInverted(true);
plothistBottom->getXAxis()->setShowZeroAxis(false);
plothistBottom->getYAxis()->setShowZeroAxis(false);
plothistBottom->getXAxis()->setDrawMode1(JKQTPCADMLine);
plothistBottom->getXAxis()->setDrawMode2(JKQTPCADMLineTicks);
plothistBottom->setGrid(false);
plothistBottom->setMousePositionShown(false);
```
This results in a graph like this:
![jkqtplotter_simpletest_datastore_statistics_2d_marginhist](https://raw.githubusercontent.com/jkriege2/JKQtPlotter/master/screenshots/jkqtplotter_simpletest_datastore_statistics_2d_marginhist.png)
With the same method, also marginal Kernel Density Estimates can be drawn:
```.cpp
double bwx=jkqtpstatEstimateKDEBandwidth(datastore1->begin(randomdatacolx_small), datastore1->end(randomdatacolx_small));
jkqtpstatAddHKDE1DAutoranged(plotkdeBottom->getPlotter(), datastore1->begin(randomdatacolx_small), datastore1->end(randomdatacolx_small), 0.01, &jkqtpstatKernel1DGaussian, bwx);
double bwy=jkqtpstatEstimateKDEBandwidth(datastore1->begin(randomdatacoly_small), datastore1->end(randomdatacoly_small));
jkqtpstatAddVKDE1DAutoranged(plotkdeRight->getPlotter(), datastore1->begin(randomdatacoly_small), datastore1->end(randomdatacoly_small), 0.01, &jkqtpstatKernel1DGaussian, bwy);
```
![jkqtplotter_simpletest_datastore_statistics_2d_marginkde](https://raw.githubusercontent.com/jkriege2/JKQtPlotter/master/screenshots/jkqtplotter_simpletest_datastore_statistics_2d_marginkde.png)
# 2D Histogram
Calculating 2D-Histograms is implemented in `jkqtpstatHistogram2D()` from the [statisticslibrary]. This functions calculates a histogram on a given grid with the first bin at `xmin,ymin` and the last bin at `xmax,ymax`, containing `Nx,Ny` bins in x-/y-direction. You can use the result to fill new image column (with `Nx` columns and `Ny` rows) in a `JKQTPDatastore`, which can then be used to draw the histogram.
First we have to determine the x- and y-range of data and calculate the number of bins from these:
```.cpp
double xmin=0, xmax=0;
double ymin=0, ymax=0;
jkqtpstatMinMax(datastore1->begin(randomdatacolx), datastore1->end(randomdatacolx), xmin,xmax);
jkqtpstatMinMax(datastore1->begin(randomdatacoly), datastore1->end(randomdatacoly), ymin,ymax);
size_t Nx=jkqtp_ceilTo<size_t>((xmax-xmin)/2.0);
size_t Ny=jkqtp_ceilTo<size_t>((ymax-ymin)/2.0);
```
Now we can add the image column
```.cpp
size_t histcol=datastore1->addImageColumn(Nx, Ny, "2d histogram");
```
and calculate the histogram:
```.cpp
jkqtpstatHistogram2D(datastore1->begin(randomdatacolx), datastore1->end(randomdatacolx), datastore1->begin(randomdatacoly), datastore1->end(randomdatacoly),
datastore1->begin(histcol),
xmin, xmax, ymin, ymax,
Nx, Ny, true);
```
Finally the histogram is drawn using a `JKQTPColumnMathImage`:
```.cpp
JKQTPColumnMathImage* gHist;
plothist->addGraph(gHist=new JKQTPColumnMathImage(plothist));
gHist->setImageColumn(static_cast<int>(histcol));
gHist->setX(xmin);
gHist->setY(ymin);
gHist->setWidth(xmax-xmin);
gHist->setHeight(ymax-ymin);
gHist->setTitle("2D Histogram");
```
The resulting image plot looks like this:
![jkqtplotter_simpletest_datastore_statistics_2d_hist](https://raw.githubusercontent.com/jkriege2/JKQtPlotter/master/screenshots/jkqtplotter_simpletest_datastore_statistics_2d_hist.png)
There is also an "adaptor" which significanty reduce the amount of coude you have to type (i.e. it replaces all the code above):
```.cpp
jkqtpstatAddHistogram2DImage(plothist->getPlotter(), datastore1->begin(randomdatacolx), datastore1->end(randomdatacolx), datastore1->begin(randomdatacoly), datastore1->end(randomdatacoly), Nx, Ny, true);
jkqtpstatAddHistogram2DContour(plothist->getPlotter(), datastore1->begin(randomdatacolx), datastore1->end(randomdatacolx), datastore1->begin(randomdatacoly), datastore1->end(randomdatacoly), Nx, Ny, true);
```
These differ in the type of plot that is added `jkqtpstatAddHistogram2DImage()` adds an image graph (`JKQTPColumnMathImage`) and `jkqtpstatAddHistogram2DContour()` adds a contour plot (`JKQTPColumnContourPlot`). A contour plot may look like this:
![jkqtplotter_simpletest_datastore_statistics_2d_histcontour](https://raw.githubusercontent.com/jkriege2/JKQtPlotter/master/screenshots/jkqtplotter_simpletest_datastore_statistics_2d_histcontour.png)
# Kernel Density Estimates (KDE)
Especially when only few samples from a distribution are available, histograms are not good at representing the underlying data distribution. In such cases, [Kernel Density Estimates (KDE)](https://en.wikipedia.org/wiki/Multivariate_kernel_density_estimation) can help, which are basically a smoothed variant of a histogram. The [statisticslibrary] supports calculating them via e.g. `jkqtpstatKDE2D()`:
This functions calculates a histogram on a given grid with the first bin at `xmin,ymin` and the last bin at `xmax,ymax`, containing `Nx,Ny` bins in x-/y-direction. You can use the result to fill new image column (with `Nx` columns and `Ny` rows) in a `JKQTPDatastore`, which can then be used to draw the histogram.
First we have to determine the x- and y-range of data and calculate the number of bins from these:
```.cpp
double xmin=0, xmax=0;
double ymin=0, ymax=0;
jkqtpstatMinMax(datastore1->begin(randomdatacolx_small), datastore1->end(randomdatacolx_small), xmin,xmax);
jkqtpstatMinMax(datastore1->begin(randomdatacoly_small), datastore1->end(randomdatacoly_small), ymin,ymax);
Nx=jkqtp_ceilTo<size_t>((xmax-xmin)/0.1);
Ny=jkqtp_ceilTo<size_t>((ymax-ymin)/0.1);
```
We can also estimate the KDE-bandwidth for both directions now:
```.cpp
bwx=jkqtpstatEstimateKDEBandwidth2D(datastore1->begin(randomdatacolx_small), datastore1->end(randomdatacolx_small));
bwy=jkqtpstatEstimateKDEBandwidth2D(datastore1->begin(randomdatacoly_small), datastore1->end(randomdatacoly_small));
```
Now we can add the image column
```.cpp
size_t kdecol=datastore1->addImageColumn(Nx, Ny, "2d KDE");
```
and calculate the histogram:
```.cpp
jkqtpstatKDE2D(datastore1->begin(randomdatacolx_small), datastore1->end(randomdatacolx_small), datastore1->begin(randomdatacoly_small), datastore1->end(randomdatacoly_small),
datastore1->begin(kdecol),
xmin, xmax, ymin, ymax, Nx, Ny,
&jkqtpstatKernel2DGaussian, bwx, bwy);
```
Finally the histogram is drawn using a `JKQTPColumnMathImage`:
```.cpp
JKQTPColumnMathImage* gKDE;
plotkde->addGraph(gKDE=new JKQTPColumnMathImage(plotkde));
gKDE->setImageColumn(static_cast<int>(kdecol));
gKDE->setX(xmin);
gKDE->setY(ymin);
gKDE->setWidth(xmax-xmin);
gKDE->setHeight(ymax-ymin);
gKDE->setTitle("2D KDE");
```
The resulting plot image looks like this:
![jkqtplotter_simpletest_datastore_statistics_2d_kde](https://raw.githubusercontent.com/jkriege2/JKQtPlotter/master/screenshots/jkqtplotter_simpletest_datastore_statistics_2d_kde.png)
Note that calculating a KDE is a rather time-consuming task. The KDE in the image above took about 8s to calculate (from 150 datapairs and on a 193*267 pixel grid), whereas the corresponding histogram took only 1ms to calculate!
There is also an "adaptor" which significanty reduce the amount of coude you have to type (i.e. it replaces all the code above):
```.cpp
jkqtpstatAddKDE2DImage(plotkde->getPlotter(), datastore1->begin(randomdatacolx_small), datastore1->end(randomdatacolx_small), datastore1->begin(randomdatacoly_small), datastore1->end(randomdatacoly_small), Nx, Ny, &jkqtpstatKernel2DGaussian, bwx, bwy);
jkqtpstatAddKDE2DContour(plotkde->getPlotter(), datastore1->begin(randomdatacolx_small), datastore1->end(randomdatacolx_small), datastore1->begin(randomdatacoly_small), datastore1->end(randomdatacoly_small), Nx, Ny, &jkqtpstatKernel2DGaussian, bwx, bwy);
```
These differ in the type of plot that is added `jkqtpstatAddKDE2DImage()` adds an image graph (`JKQTPColumnMathImage`) and `jkqtpstatAddKDE2DContour()` adds a contour plot (`JKQTPColumnContourPlot`). the first is shown in the screenshot above, the latter looks like this:
![jkqtplotter_simpletest_datastore_statistics_2d_kdecontour](https://raw.githubusercontent.com/jkriege2/JKQtPlotter/master/screenshots/jkqtplotter_simpletest_datastore_statistics_2d_kdecontour.png)
# Screenshot of the full Program
The output of the full test program [`jkqtplotter_simpletest_datastore_statistics_2d.cpp`](https://github.com/jkriege2/JKQtPlotter/tree/master/examples/simpletest_datastore_statistics_2d/jkqtplotter_simpletest_datastore_statistics_2d.cpp) looks like this:
![jkqtplotter_simpletest_datastore_statistics_2d](https://raw.githubusercontent.com/jkriege2/JKQtPlotter/master/screenshots/jkqtplotter_simpletest_datastore_statistics_2d.png)

View File

@ -0,0 +1,277 @@
/** \example jkqtplotter_simpletest_datastore_statistics_2d.cpp
* Explains how to use the internal statistics library (see \ref jkqtptools_statistics ) together with JKQTPDatastore to generate advanced plots for 2-dimensional data.
*
* \ref JKQTPlotterBasicJKQTPDatastoreStatistics2D
*/
#include <QApplication>
#include "jkqtplotter/jkqtplotter.h"
#include "jkqtplotter/jkqtpgraphsimage.h"
#include "jkqtplotter/jkqtpgraphsstatisticsadaptors.h"
#include "jkqtplotter/jkqtpgraphsgeometric.h"
#include "jkqtplotter/jkqtpgraphsscatter.h"
#include "jkqtcommon/jkqtpstatisticstools.h"
#include "jkqtcommon/jkqtpstringtools.h"
#include <random>
#include <cmath>
int main(int argc, char* argv[])
{
QApplication app(argc, argv);
QElapsedTimer timer;
// 1. create a window with several plotters and get a pointer to the internal datastores (for convenience)
QWidget mainWidget;
QGridLayout* lay;
mainWidget.setLayout(lay=new QGridLayout);
JKQTPlotter* plothist=new JKQTPlotter(&mainWidget);
lay->addWidget(plothist,0,1);
plothist->getPlotter()->setPlotLabel("Histograms");
JKQTPDatastore* datastore1=plothist->getDatastore();
JKQTPlotter* plothistLeft=new JKQTPlotter(datastore1, &mainWidget);
lay->addWidget(plothistLeft,0,0);
JKQTPlotter* plothistBottom=new JKQTPlotter(datastore1, &mainWidget);
lay->addWidget(plothistBottom,1,1);
plothistLeft->synchronizeYToMaster(plothist);
plothistLeft->setAbsoluteX(0,1);
plothistLeft->getXAxis()->setInverted(true);
plothistLeft->getXAxis()->setShowZeroAxis(false);
plothistLeft->getYAxis()->setShowZeroAxis(false);
plothistLeft->getYAxis()->setDrawMode1(JKQTPCADMLine);
plothistLeft->getYAxis()->setDrawMode2(JKQTPCADMLineTicks);
plothistLeft->setGrid(false);
plothistBottom->synchronizeXToMaster(plothist);
plothistBottom->setAbsoluteY(0,1);
plothistBottom->getYAxis()->setInverted(true);
plothistBottom->getXAxis()->setShowZeroAxis(false);
plothistBottom->getYAxis()->setShowZeroAxis(false);
plothistBottom->getXAxis()->setDrawMode1(JKQTPCADMLine);
plothistBottom->getXAxis()->setDrawMode2(JKQTPCADMLineTicks);
plothistBottom->setGrid(false);
plothistBottom->setMousePositionShown(false);
JKQTPlotter* plotkde=new JKQTPlotter(datastore1, &mainWidget);
plotkde->getPlotter()->setPlotLabel("Kernel Density Estimate");
lay->addWidget(plotkde,0,2);
JKQTPlotter* plotkdeRight=new JKQTPlotter(datastore1, &mainWidget);
lay->addWidget(plotkdeRight,0,3);
JKQTPlotter* plotkdeBottom=new JKQTPlotter(datastore1, &mainWidget);
lay->addWidget(plotkdeBottom,1,2);
plotkdeRight->synchronizeYToMaster(plotkde);
plotkdeRight->setAbsoluteX(0,1);
plotkdeRight->getXAxis()->setShowZeroAxis(false);
plotkdeRight->getYAxis()->setShowZeroAxis(false);
plotkdeRight->getYAxis()->setDrawMode1(JKQTPCADMLine);
plotkdeRight->getYAxis()->setDrawMode2(JKQTPCADMLineTicks);
plotkdeRight->setGrid(false);
plotkdeBottom->synchronizeXToMaster(plotkde);
plotkdeBottom->setAbsoluteY(0,1);
plotkdeBottom->getYAxis()->setInverted(true);
plotkdeBottom->getXAxis()->setShowZeroAxis(false);
plotkdeBottom->getYAxis()->setShowZeroAxis(false);
plotkdeBottom->getXAxis()->setDrawMode1(JKQTPCADMLine);
plotkdeBottom->getXAxis()->setDrawMode2(JKQTPCADMLineTicks);
plotkdeBottom->setGrid(false);
plotkdeBottom->setMousePositionShown(false);
lay->setColumnStretch(0,1);
lay->setColumnStretch(1,3);
lay->setColumnStretch(2,3);
lay->setColumnStretch(3,1);
lay->setRowStretch(0,3);
lay->setRowStretch(1,1);
// 2.1. Now we create two vectors with random values
// randomdatacolx: random x-positions, drawn from one of two gaussian distributions
// randomdatacoly: random y-positions, drawn from one of two gaussian distributions
// randomdatacoldist: indicates, which if the two sets of gaussian distributions was chosen for each datapoint
size_t randomdatacolx_small=datastore1->addColumn("random data, x");
size_t randomdatacoly_small=datastore1->addColumn("random data, y");
size_t randomdatacoldist_small=datastore1->addColumn("random data, distribution/class");
size_t randomdatacolx=datastore1->addColumn("random data, x");
size_t randomdatacoly=datastore1->addColumn("random data, y");
size_t randomdatacoldist=datastore1->addColumn("random data, distribution/class");
// random number generators:
std::random_device rd;
std::mt19937 gen{rd()};
std::uniform_int_distribution<> ddecide(0,2);
std::normal_distribution<> d1x{5,3};
std::normal_distribution<> d1y{5,1};
std::normal_distribution<> d2x{10,2};
std::normal_distribution<> d2y{10,5};
for (size_t i=0; i<500; i++) {
double rx=0,ry=0;
const int decide=ddecide(gen);
if (decide==0) {
rx=d1x(gen);
ry=d1y(gen);
} else {
rx=d2x(gen);
ry=d2y(gen);
}
if (i<150) {
datastore1->appendToColumn(randomdatacolx_small, rx);
datastore1->appendToColumn(randomdatacoly_small, ry);
datastore1->appendToColumn(randomdatacoldist_small, std::min(1,decide));
}
datastore1->appendToColumn(randomdatacolx, rx);
datastore1->appendToColumn(randomdatacoly, ry);
datastore1->appendToColumn(randomdatacoldist, std::min(1,decide));
}
// 2.2. To visualize the data, a simple JKQTPPeakStreamGraph is used:
JKQTPXYParametrizedScatterGraph* gDataHist;
plothist->addGraph(gDataHist=new JKQTPXYParametrizedScatterGraph(plothist));
gDataHist->setXYColumns(randomdatacolx,randomdatacoly);
gDataHist->setSymbolColumn(randomdatacoldist);
QMap<double, JKQTPGraphSymbols> mapped;
mapped[0]=JKQTPGraphSymbols::JKQTPCross;
mapped[1]=JKQTPGraphSymbols::JKQTPPlus;
gDataHist->setMappedSymbolColumnFunctor(mapped);
gDataHist->setSymbolSize(5);
gDataHist->setSymbolColor(QColorWithAlphaF(QColor("red"), 0.7));
gDataHist->setDrawLine(false);
gDataHist->setTitle(QString("random data, $N="+QString::number(datastore1->getRows(randomdatacoldist))+"$"));
JKQTPXYParametrizedScatterGraph* gDataKDE;
plotkde->addGraph(gDataKDE=new JKQTPXYParametrizedScatterGraph(plotkde));
gDataKDE->setXYColumns(randomdatacolx_small,randomdatacoly_small);
gDataKDE->setSymbolColumn(randomdatacoldist_small);
gDataKDE->setSymbolSize(3);
gDataKDE->setSymbolColor(QColorWithAlphaF(QColor("red"), 0.7));
gDataKDE->setMappedSymbolColumnFunctor(mapped);
gDataKDE->setDrawLine(false);
gDataKDE->setTitle(QString("random data, $N="+QString::number(datastore1->getRows(randomdatacoldist_small))+"$"));
// 2.3. to visualize the initial distributions, we draw an ellipse indicating the
// variance of the distributions
JKQTPGeoEllipse* gEll1Hist;
JKQTPGeoEllipse* gEll2Hist;
JKQTPGeoEllipse* gEll1KDE;
JKQTPGeoEllipse* gEll2KDE;
plothist->addGraph(gEll1Hist=new JKQTPGeoEllipse(plothist, d1x.mean(), d1y.mean(),d1x.stddev()*2.0,d1y.stddev()*2.0));
plothist->addGraph(gEll2Hist=new JKQTPGeoEllipse(plothist, d2x.mean(), d2y.mean(),d2x.stddev()*2.0,d2y.stddev()*2.0));
plotkde->addGraph(gEll1KDE=new JKQTPGeoEllipse(plothist, d1x.mean(), d1y.mean(),d1x.stddev()*2.0,d1y.stddev()*2.0));
plotkde->addGraph(gEll2KDE=new JKQTPGeoEllipse(plothist, d2x.mean(), d2y.mean(),d2x.stddev()*2.0,d2y.stddev()*2.0));
// 3. Marginal (1D) Statistics of the x-position and y-position deistributions:
// 3.1. First we calculate the x/y marginal histograms, as desribed in https://github.com/jkriege2/JKQtPlotter/tree/master/examples/simpletest_datastore_statistics
timer.start();
jkqtpstatAddHHistogram1DAutoranged(plothistBottom->getPlotter(), datastore1->begin(randomdatacolx), datastore1->end(randomdatacolx), 1.0, true);
jkqtpstatAddVHistogram1DAutoranged(plothistLeft->getPlotter(), datastore1->begin(randomdatacoly), datastore1->end(randomdatacoly), 1.0, true);
qDebug()<<"histogram, 1D: "<<timer.elapsed()/2.0<<"ms";
// 3.2. Also we calculate the x/y marginal kernel density estimates, as desribed in https://github.com/jkriege2/JKQtPlotter/tree/master/examples/simpletest_datastore_statistics
timer.start();
double bwx=jkqtpstatEstimateKDEBandwidth(datastore1->begin(randomdatacolx_small), datastore1->end(randomdatacolx_small));
qDebug()<<bwx;
jkqtpstatAddHKDE1DAutoranged(plotkdeBottom->getPlotter(), datastore1->begin(randomdatacolx_small), datastore1->end(randomdatacolx_small), 0.01, &jkqtpstatKernel1DGaussian, bwx);
double bwy=jkqtpstatEstimateKDEBandwidth(datastore1->begin(randomdatacoly_small), datastore1->end(randomdatacoly_small));
qDebug()<<bwy;
jkqtpstatAddVKDE1DAutoranged(plotkdeRight->getPlotter(), datastore1->begin(randomdatacoly_small), datastore1->end(randomdatacoly_small), 0.01, &jkqtpstatKernel1DGaussian, bwy);
qDebug()<<"KDE+bandwidth, 1D: "<<timer.elapsed()/2.0<<"ms";
// 4. 2D Histogram
double xmin=0, xmax=0;
double ymin=0, ymax=0;
timer.start();
jkqtpstatMinMax(datastore1->begin(randomdatacolx), datastore1->end(randomdatacolx), xmin,xmax);
jkqtpstatMinMax(datastore1->begin(randomdatacoly), datastore1->end(randomdatacoly), ymin,ymax);
size_t Nx=jkqtp_ceilTo<size_t>((xmax-xmin)/2.0);
size_t Ny=jkqtp_ceilTo<size_t>((ymax-ymin)/2.0);
size_t histcol=datastore1->addImageColumn(Nx, Ny, "2d histogram");
jkqtpstatHistogram2D(datastore1->begin(randomdatacolx), datastore1->end(randomdatacolx), datastore1->begin(randomdatacoly), datastore1->end(randomdatacoly),
datastore1->begin(histcol),
xmin, xmax, ymin, ymax,
Nx, Ny, true);
qDebug()<<"histogram, 2D: "<<timer.elapsed()<<"ms";
JKQTPColumnMathImage* gHist;
plothist->addGraph(gHist=new JKQTPColumnMathImage(plothist));
gHist->setImageColumn(static_cast<int>(histcol));
gHist->setX(xmin);
gHist->setY(ymin);
gHist->setWidth(xmax-xmin);
gHist->setHeight(ymax-ymin);
gHist->setTitle("2D Histogram");
qDebug()<<xmin<<xmax<<Nx;
qDebug()<<ymin<<ymax<<Ny;
// There also exist "adaptors", which execute the complete code above in one call.
// Two flavors exist:
// jkqtpstatAddHistogram2DImage() adds an image plot as shown above
// jkqtpstatAddHistogram2DContour() adds a contour plot
//jkqtpstatAddHistogram2DImage(plothist->getPlotter(), datastore1->begin(randomdatacolx), datastore1->end(randomdatacolx), datastore1->begin(randomdatacoly), datastore1->end(randomdatacoly), Nx, Ny, true);
//jkqtpstatAddHistogram2DContour(plothist->getPlotter(), datastore1->begin(randomdatacolx), datastore1->end(randomdatacolx), datastore1->begin(randomdatacoly), datastore1->end(randomdatacoly), size_t(50),size_t(50), true);
// 5. 2D KDE
xmin=0; xmax=0;
ymin=0; ymax=0;
timer.start();
jkqtpstatMinMax(datastore1->begin(randomdatacolx_small), datastore1->end(randomdatacolx_small), xmin,xmax);
jkqtpstatMinMax(datastore1->begin(randomdatacoly_small), datastore1->end(randomdatacoly_small), ymin,ymax);
Nx=jkqtp_ceilTo<size_t>((xmax-xmin)/0.1);
Ny=jkqtp_ceilTo<size_t>((ymax-ymin)/0.1);
bwx=jkqtpstatEstimateKDEBandwidth2D(datastore1->begin(randomdatacolx_small), datastore1->end(randomdatacolx_small));
qDebug()<<bwx;
bwy=jkqtpstatEstimateKDEBandwidth2D(datastore1->begin(randomdatacoly_small), datastore1->end(randomdatacoly_small));
qDebug()<<bwy;
qDebug()<<xmin<<xmax<<Nx;
qDebug()<<ymin<<ymax<<Ny;
size_t kdecol=datastore1->addImageColumn(Nx, Ny, "2d KDE");
jkqtpstatKDE2D(datastore1->begin(randomdatacolx_small), datastore1->end(randomdatacolx_small), datastore1->begin(randomdatacoly_small), datastore1->end(randomdatacoly_small),
datastore1->begin(kdecol),
xmin, xmax, ymin, ymax, Nx, Ny,
&jkqtpstatKernel2DGaussian, bwx, bwy);
qDebug()<<"KDE, 2D: "<<timer.elapsed()<<"ms";
JKQTPColumnMathImage* gKDE;
plotkde->addGraph(gKDE=new JKQTPColumnMathImage(plotkde));
gKDE->setImageColumn(static_cast<int>(kdecol));
gKDE->setX(xmin);
gKDE->setY(ymin);
gKDE->setWidth(xmax-xmin);
gKDE->setHeight(ymax-ymin);
gKDE->setTitle("2D KDE");
// There also exist "adaptors", which execute the complete code above in one call.
// Two flavors exist:
// jkqtpstatAddKDE2DImage() adds an image plot as shown above
// jkqtpstatAddKDE2DContour() adds a contour plot
//jkqtpstatAddKDE2DImage(plotkde->getPlotter(), datastore1->begin(randomdatacolx_small), datastore1->end(randomdatacolx_small), datastore1->begin(randomdatacoly_small), datastore1->end(randomdatacoly_small), Nx, Ny, &jkqtpstatKernel2DGaussian, bwx, bwy);
//jkqtpstatAddKDE2DContour(plotkde->getPlotter(), datastore1->begin(randomdatacolx_small), datastore1->end(randomdatacolx_small), datastore1->begin(randomdatacoly_small), datastore1->end(randomdatacoly_small), Nx, Ny, &jkqtpstatKernel2DGaussian, bwx, bwy);
// autoscale the plot so the graph is contained
plothist->zoomToFit();
plothist->setGrid(false);
plothist->getXAxis()->setShowZeroAxis(false);
plothist->getYAxis()->setShowZeroAxis(false);
plothist->getPlotter()->setKeyBackgroundColor(QColorWithAlphaF("white", 0.25), Qt::SolidPattern);
plothist->getPlotter()->moveGraphTop(gDataHist);
plothist->getPlotter()->moveGraphTop(gEll1Hist);
plothist->getPlotter()->moveGraphTop(gEll2Hist);
plothistBottom->zoomToFit(false, true);
plothistLeft->zoomToFit(true, false);
plotkde->zoomToFit();
plotkde->setGrid(false);
plotkde->getXAxis()->setShowZeroAxis(false);
plotkde->getYAxis()->setShowZeroAxis(false);
plotkde->getPlotter()->setKeyBackgroundColor(QColorWithAlphaF("white", 0.25), Qt::SolidPattern);
plotkde->getPlotter()->moveGraphTop(gDataKDE);
plotkde->getPlotter()->moveGraphTop(gEll1KDE);
plotkde->getPlotter()->moveGraphTop(gEll2KDE);
plotkdeBottom->zoomToFit(false, true);
plotkdeRight->zoomToFit(true, false);
// show plotter and make it a decent size
mainWidget.show();
mainWidget.resize(1200,600);
return app.exec();
}

View File

@ -0,0 +1,27 @@
# source code for this simple demo
SOURCES = jkqtplotter_simpletest_datastore_statistics_2d.cpp
# configure Qt
CONFIG += link_prl qt
QT += core gui xml svg
greaterThan(QT_MAJOR_VERSION, 4): QT += widgets printsupport
# output executable name
TARGET = jkqtplotter_simpletest_datastore_statistics_2d
# include JKQTPlotter source headers and link against library
DEPENDPATH += ../../lib ../../staticlib/jkqtplotterlib
INCLUDEPATH += ../../lib
CONFIG (debug, debug|release) {
LIBS += -L../../staticlib/jkqtplotterlib/debug -ljkqtplotterlib_debug
} else {
LIBS += -L../../staticlib/jkqtplotterlib/release -ljkqtplotterlib
}
message("LIBS = $$LIBS")
win32-msvc*: DEFINES += _USE_MATH_DEFINES
win32-msvc*: DEFINES += NOMINMAX

View File

@ -0,0 +1,8 @@
TEMPLATE = subdirs
SUBDIRS += jkqtplotterlib jkqtplotter_simpletest_datastore_statistics_2d
jkqtplotterlib.file = ../../staticlib/jkqtplotterlib/jkqtplotterlib.pro
jkqtplotter_simpletest_datastore_statistics_2d.file=$$PWD/jkqtplotter_simpletest_datastore_statistics_2d.pro
jkqtplotter_simpletest_datastore_statistics_2d.depends = jkqtplotterlib

View File

@ -12,7 +12,7 @@
#include "jkqtplotter/jkqtplotter.h"
#include "jkqtplotter/jkqtpgraphsscatter.h"
#include "jkqtplotter/jkqtpgraphsgeometric.h"
#include "jkqtcommon/jkqtptools.h"
#include "jkqtplotter/jkqtptools.h"
#include "jkqtplotter/jkqtpgraphsfilledcurve.h"
void drawWithDateAxis(JKQTPlotter& plot) {

View File

@ -21,6 +21,8 @@ message("LIBS = $$LIBS")
win32-msvc*: DEFINES += _USE_MATH_DEFINES
win32-msvc*: DEFINES += NOMINMAX

View File

@ -1,6 +1,11 @@
# Example (JKQTPlotter): Different Types of Errorindicators {#JKQTPlotterErrorBarStyles}
This project (see `./examples/simpletest_errorbarstyles/`) simply creates a JKQTPlotter widget (as a new window) and adds several curves show-casing different styles of error indicators. Data is initialized from two QVector<double> objects.
[JKQTPlotterBasicJKQTPDatastoreStatisticsGroupedStat]: @ref JKQTPlotterBasicJKQTPDatastoreStatisticsGroupedStat "1-Dimensional Group Statistics with JKQTPDatastore"
[statisticslibrary]: @ref jkqtptools_math_statistics "JKQTPlotter Statistics Library"
***Note:*** This examples explains how to plot graphs with error indicators, when the data has already been calculated. The tutorial [JKQTPlotterBasicJKQTPDatastoreStatisticsGroupedStat] explains one way how to use the [statisticslibrary] in order to calculate the errors from data.
The source code of the main application can be found in [`jkqtplotter_simpletest_errorbarstyles.cpp`](https://github.com/jkriege2/JKQtPlotter/tree/master/examples/simpletest_errorbarstyles/jkqtplotter_simpletest_errorbarstyles.cpp).
First some data is added to the internal datastore (mostly, like explained in several other examples, like e.g. [Line Graph with Different Symbols and Line Styles](https://github.com/jkriege2/JKQtPlotter/tree/master/examples/simpletest_symbols_and_styles)). The (in a loop) several graphs are added, each with a distinct style for its error indicators:

View File

@ -7,7 +7,7 @@
#include <QApplication>
#include "jkqtplotter/jkqtplotter.h"
#include "jkqtplotter/jkqtpgraphsscatter.h"
#include "jkqtcommon/jkqtptools.h"
#include "jkqtplotter/jkqtptools.h"
int main(int argc, char* argv[])

View File

@ -20,5 +20,7 @@ CONFIG (debug, debug|release) {
message("LIBS = $$LIBS")
win32-msvc*: DEFINES += _USE_MATH_DEFINES
win32-msvc*: DEFINES += NOMINMAX

View File

@ -22,6 +22,8 @@ CONFIG (debug, debug|release) {
}
message("LIBS = $$LIBS")
win32-msvc*: DEFINES += _USE_MATH_DEFINES
win32-msvc*: DEFINES += NOMINMAX

View File

@ -21,6 +21,7 @@ CONFIG (debug, debug|release) {
message("LIBS = $$LIBS")
win32-msvc*: DEFINES += _USE_MATH_DEFINES
win32-msvc*: DEFINES += NOMINMAX

View File

@ -19,6 +19,8 @@ CONFIG (debug, debug|release) {
}
message("LIBS = $$LIBS")
win32-msvc*: DEFINES += _USE_MATH_DEFINES
win32-msvc*: DEFINES += NOMINMAX

View File

@ -19,6 +19,8 @@ CONFIG (debug, debug|release) {
}
message("LIBS = $$LIBS")
win32-msvc*: DEFINES += _USE_MATH_DEFINES
win32-msvc*: DEFINES += NOMINMAX

View File

@ -1,4 +1,4 @@
# Example (JKQTPlotter): Simple math image plot {#JKQTPlotterImagePlotModifier}
# Example (JKQTPlotter): Simple math image plot with modifier datat {#JKQTPlotterImagePlotModifier}
This project (see `./examples/simpletest_imageplot_modifier/`) creates a JKQTPlotter widget (as a new window) and adds a color-coded image plot of a mathematical function (here `sin(r)`). Then a second image (linearly scaling from 1 in the center to 0 at the borders) is used to modify the first image. The modification can alter several properties of the original image, like its saturation, its transparency (alpha) ...
The source code of the main application is (see [`jkqtplotter_simpletest_imageplot_modifier.cpp`](https://github.com/jkriege2/JKQtPlotter/tree/master/examples/simpletest_imageplot_modifier/jkqtplotter_simpletest_imageplot_modifier.cpp):

View File

@ -19,6 +19,8 @@ CONFIG (debug, debug|release) {
}
message("LIBS = $$LIBS")
win32-msvc*: DEFINES += _USE_MATH_DEFINES
win32-msvc*: DEFINES += NOMINMAX

View File

@ -31,6 +31,8 @@ message("LIBS = $$LIBS")
# To fix error: C2338: va_start argument must not
# have reference type and must not be parenthesized
DEFINES += _CRT_NO_VA_START_VALIDATION
# fix errors with min()/max() macros from windows
DEFINES += NOMINMAX
}

View File

@ -32,3 +32,6 @@ CONFIG (debug, debug|release) {
message("LIBS = $$LIBS")
INSTALLS += opencvdlls
win32-msvc*: DEFINES += _USE_MATH_DEFINES
win32-msvc*: DEFINES += NOMINMAX

View File

@ -21,6 +21,7 @@ CONFIG (debug, debug|release) {
message("LIBS = $$LIBS")
win32-msvc*: DEFINES += _USE_MATH_DEFINES
win32-msvc*: DEFINES += NOMINMAX

View File

@ -8,7 +8,7 @@
#include "jkqtplotter/jkqtplotter.h"
#include "jkqtplotter/jkqtpgraphsscatter.h"
#include "jkqtplotter/jkqtpgraphsgeometric.h"
#include "jkqtcommon/jkqtptools.h"
#include "jkqtplotter/jkqtptools.h"
#define sqr(x) ((x)*(x))

Some files were not shown because too many files have changed in this diff Show More