Merge branch 'master' into cmake_build
@ -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)
|
||||
|
||||
|
@ -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
|
||||
|
@ -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>
|
||||
|
||||
|
||||
|
@ -12,6 +12,8 @@
|
||||
- \ref JKQTMathTextSimpleExample
|
||||
.
|
||||
|
||||
\defgroup jkqtmathtext_items JKQTMathText Render-Tree Items
|
||||
\ingroup jkqtmathtext
|
||||
|
||||
|
||||
*/
|
@ -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
|
||||
|
@ -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"
|
||||
|
@ -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>
|
||||
|
Before Width: | Height: | Size: 32 KiB After Width: | Height: | Size: 73 KiB |
Before Width: | Height: | Size: 13 KiB After Width: | Height: | Size: 26 KiB |
BIN
doc/images/JKQTPGraphViolinplot_BoxViolin.png
Normal file
After Width: | Height: | Size: 3.0 KiB |
BIN
doc/images/JKQTPGraphViolinplot_HBoxViolin.png
Normal file
After Width: | Height: | Size: 3.4 KiB |
BIN
doc/images/JKQTPGraphViolinplot_HSmoothViolin.png
Normal file
After Width: | Height: | Size: 7.3 KiB |
BIN
doc/images/JKQTPGraphViolinplot_HStepViolin.png
Normal file
After Width: | Height: | Size: 3.7 KiB |
BIN
doc/images/JKQTPGraphViolinplot_SmoothViolin.png
Normal file
After Width: | Height: | Size: 7.4 KiB |
BIN
doc/images/JKQTPGraphViolinplot_StepViolin.png
Normal file
After Width: | Height: | Size: 3.1 KiB |
BIN
doc/images/JKQTPGraphViolinplot_ViolinBoth.png
Normal file
After Width: | Height: | Size: 7.4 KiB |
BIN
doc/images/JKQTPGraphViolinplot_ViolinBottom.png
Normal file
After Width: | Height: | Size: 3.7 KiB |
BIN
doc/images/JKQTPGraphViolinplot_ViolinHBoth.png
Normal file
After Width: | Height: | Size: 5.9 KiB |
BIN
doc/images/JKQTPGraphViolinplot_ViolinLeft.png
Normal file
After Width: | Height: | Size: 11 KiB |
BIN
doc/images/JKQTPGraphViolinplot_ViolinRight.png
Normal file
After Width: | Height: | Size: 5.0 KiB |
BIN
doc/images/JKQTPGraphViolinplot_ViolinTop.png
Normal file
After Width: | Height: | Size: 3.6 KiB |
BIN
doc/images/JKQTPSingleColumnSymbolsGraph_RugPlot.png
Normal file
After Width: | Height: | Size: 4.1 KiB |
BIN
doc/images/JKQTPViolinplotHorizontalElement_elements.png
Normal file
After Width: | Height: | Size: 22 KiB |
BIN
doc/images/JKQTPViolinplotVerticalElement.png
Normal file
After Width: | Height: | Size: 16 KiB |
BIN
doc/images/JKQTPViolinplotVerticalElement_small.png
Normal file
After Width: | Height: | Size: 11 KiB |
BIN
doc/images/jkqtpstatAddHViolinplotHistogram.png
Normal file
After Width: | Height: | Size: 3.0 KiB |
BIN
doc/images/jkqtpstatAddHViolinplotHistogramAndOutliers.png
Normal file
After Width: | Height: | Size: 3.5 KiB |
BIN
doc/images/jkqtpstatAddHViolinplotKDE.png
Normal file
After Width: | Height: | Size: 5.4 KiB |
BIN
doc/images/jkqtpstatAddHViolinplotKDEAndOutliers.png
Normal file
After Width: | Height: | Size: 4.4 KiB |
BIN
doc/images/jkqtpstatAddVViolinplotHistogram.png
Normal file
After Width: | Height: | Size: 2.9 KiB |
BIN
doc/images/jkqtpstatAddVViolinplotHistogramAndOutliers.png
Normal file
After Width: | Height: | Size: 3.3 KiB |
BIN
doc/images/jkqtpstatAddVViolinplotKDE.png
Normal file
After Width: | Height: | Size: 5.2 KiB |
BIN
doc/images/jkqtpstatAddVViolinplotKDEAndOutliers.png
Normal file
After Width: | Height: | Size: 5.2 KiB |
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -4,7 +4,7 @@
|
||||
#include <QWidget>
|
||||
#include <QStringList>
|
||||
#include "jkqtmathtext/jkqtmathtext.h"
|
||||
#include "jkqtplottertools/jkqtphighrestimer.h"
|
||||
#include "jkqtcommon/jkqtphighrestimer.h"
|
||||
#include <QPainter>
|
||||
#include <QTreeWidget>
|
||||
|
||||
|
@ -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
|
||||
|
@ -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);
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -17,7 +17,7 @@
|
||||
#include "jkqtplotter/jkqtpgraphsparsedfunction.h"
|
||||
#include "jkqtplotter/jkqtpbaseelements.h"
|
||||
#include "jkqtplottergui/jkqtpgraphsmodel.h"
|
||||
#include "jkqtcommon/jkqtptools.h"
|
||||
#include "jkqtplotter/jkqtptools.h"
|
||||
|
||||
|
||||
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -49,6 +49,7 @@ RESOURCES += jkqtplot_test.qrc
|
||||
|
||||
|
||||
win32-msvc*: DEFINES += _USE_MATH_DEFINES
|
||||
win32-msvc*: DEFINES += NOMINMAX
|
||||
|
||||
TARGET = jkqtplot_test
|
||||
|
||||
|
@ -20,6 +20,7 @@ CONFIG (debug, debug|release) {
|
||||
message("LIBS = $$LIBS")
|
||||
|
||||
win32-msvc*: DEFINES += _USE_MATH_DEFINES
|
||||
win32-msvc*: DEFINES += NOMINMAX
|
||||
|
||||
|
||||
|
||||
|
@ -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
|
||||
}
|
||||
|
||||
|
||||
|
@ -20,6 +20,8 @@ CONFIG (debug, debug|release) {
|
||||
message("LIBS = $$LIBS")
|
||||
|
||||
|
||||
win32-msvc*: DEFINES += _USE_MATH_DEFINES
|
||||
win32-msvc*: DEFINES += NOMINMAX
|
||||
|
||||
|
||||
|
||||
|
@ -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:
|
||||
|
@ -21,5 +21,7 @@ message("LIBS = $$LIBS")
|
||||
|
||||
|
||||
|
||||
win32-msvc*: DEFINES += _USE_MATH_DEFINES
|
||||
win32-msvc*: DEFINES += NOMINMAX
|
||||
|
||||
|
||||
|
131
examples/simpletest_contourplot/README.md
Normal 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.
|
||||
|
51
examples/simpletest_contourplot/contourplotanimator.cpp
Normal 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()));
|
||||
}
|
35
examples/simpletest_contourplot/contourplotanimator.h
Normal 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
|
@ -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();
|
||||
}
|
@ -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
|
||||
|
||||
|
||||
|
||||
|
@ -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
|
@ -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).
|
@ -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();
|
||||
|
||||
|
@ -20,6 +20,7 @@ CONFIG (debug, debug|release) {
|
||||
message("LIBS = $$LIBS")
|
||||
|
||||
win32-msvc*: DEFINES += _USE_MATH_DEFINES
|
||||
win32-msvc*: DEFINES += NOMINMAX
|
||||
|
||||
|
||||
|
||||
|
265
examples/simpletest_datastore_groupedstat/README.md
Normal 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)
|
||||
|
||||
|
@ -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();
|
||||
}
|
@ -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
|
||||
|
||||
|
||||
|
||||
|
@ -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
|
114
examples/simpletest_datastore_iterators/README.md
Normal 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)
|
||||
|
@ -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();
|
||||
}
|
@ -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
|
||||
|
||||
|
||||
|
||||
|
@ -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
|
370
examples/simpletest_datastore_regression/README.md
Normal 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)
|
||||
|
||||
|
@ -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();
|
||||
}
|
@ -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
|
||||
|
||||
|
||||
|
||||
|
@ -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
|
251
examples/simpletest_datastore_statistics/README.md
Normal 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)
|
||||
|
||||
|
@ -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();
|
||||
}
|
@ -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
|
||||
|
||||
|
||||
|
||||
|
@ -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
|
287
examples/simpletest_datastore_statistics_2d/README.md
Normal 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)
|
||||
|
||||
|
@ -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();
|
||||
}
|
@ -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
|
||||
|
||||
|
||||
|
||||
|
@ -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
|
@ -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) {
|
||||
|
@ -21,6 +21,8 @@ message("LIBS = $$LIBS")
|
||||
|
||||
|
||||
|
||||
win32-msvc*: DEFINES += _USE_MATH_DEFINES
|
||||
win32-msvc*: DEFINES += NOMINMAX
|
||||
|
||||
|
||||
|
||||
|
@ -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:
|
||||
|
@ -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[])
|
||||
|
@ -20,5 +20,7 @@ CONFIG (debug, debug|release) {
|
||||
message("LIBS = $$LIBS")
|
||||
|
||||
|
||||
win32-msvc*: DEFINES += _USE_MATH_DEFINES
|
||||
win32-msvc*: DEFINES += NOMINMAX
|
||||
|
||||
|
||||
|
@ -22,6 +22,8 @@ CONFIG (debug, debug|release) {
|
||||
}
|
||||
message("LIBS = $$LIBS")
|
||||
|
||||
win32-msvc*: DEFINES += _USE_MATH_DEFINES
|
||||
win32-msvc*: DEFINES += NOMINMAX
|
||||
|
||||
|
||||
|
||||
|
@ -21,6 +21,7 @@ CONFIG (debug, debug|release) {
|
||||
message("LIBS = $$LIBS")
|
||||
|
||||
win32-msvc*: DEFINES += _USE_MATH_DEFINES
|
||||
win32-msvc*: DEFINES += NOMINMAX
|
||||
|
||||
|
||||
|
||||
|
@ -19,6 +19,8 @@ CONFIG (debug, debug|release) {
|
||||
}
|
||||
message("LIBS = $$LIBS")
|
||||
|
||||
win32-msvc*: DEFINES += _USE_MATH_DEFINES
|
||||
win32-msvc*: DEFINES += NOMINMAX
|
||||
|
||||
|
||||
|
||||
|
@ -19,6 +19,8 @@ CONFIG (debug, debug|release) {
|
||||
}
|
||||
message("LIBS = $$LIBS")
|
||||
|
||||
win32-msvc*: DEFINES += _USE_MATH_DEFINES
|
||||
win32-msvc*: DEFINES += NOMINMAX
|
||||
|
||||
|
||||
|
||||
|
@ -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):
|
||||
|
@ -19,6 +19,8 @@ CONFIG (debug, debug|release) {
|
||||
}
|
||||
message("LIBS = $$LIBS")
|
||||
|
||||
win32-msvc*: DEFINES += _USE_MATH_DEFINES
|
||||
win32-msvc*: DEFINES += NOMINMAX
|
||||
|
||||
|
||||
|
||||
|
@ -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
|
||||
}
|
||||
|
||||
|
||||
|
@ -32,3 +32,6 @@ CONFIG (debug, debug|release) {
|
||||
message("LIBS = $$LIBS")
|
||||
|
||||
INSTALLS += opencvdlls
|
||||
|
||||
win32-msvc*: DEFINES += _USE_MATH_DEFINES
|
||||
win32-msvc*: DEFINES += NOMINMAX
|
||||
|
@ -21,6 +21,7 @@ CONFIG (debug, debug|release) {
|
||||
message("LIBS = $$LIBS")
|
||||
|
||||
win32-msvc*: DEFINES += _USE_MATH_DEFINES
|
||||
win32-msvc*: DEFINES += NOMINMAX
|
||||
|
||||
|
||||
|
||||
|
@ -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))
|
||||
|
||||
|