From ebd3e1c303226a688594c53e7c2feaaaf73b6fd4 Mon Sep 17 00:00:00 2001 From: koldo Date: Mon, 9 Dec 2019 11:32:39 +0000 Subject: [PATCH] ScatterDraw: Added histogram git-svn-id: svn://ultimatepp.org/upp/trunk@13746 f0d560ea-af0d-0410-9eb7-867de7ffcac7 --- uppsrc/ScatterDraw/DataSource.h | 3 +- uppsrc/ScatterDraw/Equation.cpp | 1 + uppsrc/ScatterDraw/Equation.h | 34 ++++++ uppsrc/ScatterDraw/Histogram.cpp | 148 +++++++++++++++++++++++++++ uppsrc/ScatterDraw/Histogram.h | 159 +++++++++++++++++++++++++++++ uppsrc/ScatterDraw/ScatterDraw.cpp | 5 +- uppsrc/ScatterDraw/ScatterDraw.h | 2 +- uppsrc/ScatterDraw/ScatterDraw.upp | 2 + 8 files changed, 350 insertions(+), 4 deletions(-) create mode 100644 uppsrc/ScatterDraw/Histogram.cpp create mode 100644 uppsrc/ScatterDraw/Histogram.h diff --git a/uppsrc/ScatterDraw/DataSource.h b/uppsrc/ScatterDraw/DataSource.h index c3eb88920..449994aaf 100644 --- a/uppsrc/ScatterDraw/DataSource.h +++ b/uppsrc/ScatterDraw/DataSource.h @@ -47,7 +47,8 @@ public: double AvgY() {return Avg(&DataSource::y);} double AvgX() {return Avg(&DataSource::x);} double RMSY() {return RMS(&DataSource::y);} - double StdDevY(double avg = Null) {return StdDev(&DataSource::y, avg);} + double StdDevY(double avg = Null) {return StdDev(&DataSource::y, avg);} + double StdDevX(double avg = Null) {return StdDev(&DataSource::x, avg);} double VarianceY(double avg = Null) {return Variance(&DataSource::y, avg);} Vector UpperEnvelopeY(double width) {return UpperEnvelope(&DataSource::y, &DataSource::x, width);} Vector LowerEnvelopeY(double width) {return LowerEnvelope(&DataSource::y, &DataSource::x, width);} diff --git a/uppsrc/ScatterDraw/Equation.cpp b/uppsrc/ScatterDraw/Equation.cpp index 4a93744b4..8d02f0f02 100644 --- a/uppsrc/ScatterDraw/Equation.cpp +++ b/uppsrc/ScatterDraw/Equation.cpp @@ -624,6 +624,7 @@ INITBLOCK { ExplicitEquation::Register("FourierEquation4"); ExplicitEquation::Register("WeibullEquation"); ExplicitEquation::Register("WeibullCumulativeEquation"); + ExplicitEquation::Register("NormalEquation"); } } \ No newline at end of file diff --git a/uppsrc/ScatterDraw/Equation.h b/uppsrc/ScatterDraw/Equation.h index c67a4aae7..a5ac3bb4a 100644 --- a/uppsrc/ScatterDraw/Equation.h +++ b/uppsrc/ScatterDraw/Equation.h @@ -366,6 +366,40 @@ private: double factor; }; +class NormalEquation : public ExplicitEquation { +public: + NormalEquation() {SetCoeff(1, 1, 1);} + NormalEquation(double c, double mean, double std) {SetCoeff(c, mean, std);} + double f(double x) { + double c = coeff[0]; + double mean = coeff[1]; + double std = coeff[2]; + return c*exp(-0.5*sqr((x - mean)/std))/(std*sqrt(2*M_PI)); + } + virtual String GetName() {return t_("Normal");} + virtual String GetEquation(int numDigits = 3) { + String c = FormatCoeff(0, numDigits); + String mean = FormatCoeff(1, numDigits); + String std = FormatCoeff(2, numDigits); + String ret = Format("%s/(%s*sqrt(2*PI))*e^(-1/2((x-%s)/%s))", c, std, mean, std); + ret.Replace("+ -", "- "); + return ret; + } + virtual void GuessCoeff(DataSource &) {} + virtual void _GuessCoeff(DataSource &series) { + double mean = series.AvgX(); + double std = series.StdDevX(); + double c = series.MaxY()*std*sqrt(2*M_PI); + SetCoeff(c, mean, std); + } + FitError Fit(DataSource &series, double &r2) { + _GuessCoeff(series); + return ExplicitEquation::Fit(series, r2); + } + FitError Fit(DataSource &series) {double dummy; return Fit(series, dummy);} + void SetDegree(int ) {NEVER();} +}; + class Rational1Equation : public ExplicitEquation { public: Rational1Equation() {SetCoeff(1, 0, 0);} diff --git a/uppsrc/ScatterDraw/Histogram.cpp b/uppsrc/ScatterDraw/Histogram.cpp new file mode 100644 index 000000000..9ea823fd5 --- /dev/null +++ b/uppsrc/ScatterDraw/Histogram.cpp @@ -0,0 +1,148 @@ +#include +#include +#include +#include + +namespace Upp { + +Histogram::Histogram() { + Clear(); +} + +Histogram::Histogram(const Histogram &hist) { + *this = hist; +} + +Histogram &Histogram::operator=(const Histogram &hist) { + totalVals = hist.totalVals; + + values = hist.values; + ranges <<= hist.ranges; + + return *this; +} + +void Histogram::Clear() { + ranges.Clear(); + values.resize(0); +} + +Histogram &Histogram::Create2D(Vector > &_ranges, Vector &data, double total) { + Clear(); + + int numAxis = 2; + int numVals = 1; + for (int ix = 0; ix < numAxis; ++ix) + numVals *= _ranges[ix].GetCount(); + valuesIdx.SetNumAxis(numAxis); + for (int ix = 0; ix < numAxis; ++ix) + valuesIdx.SetAxisDim(ix, _ranges[ix].GetCount()); + values.resize(numVals); + values.setZero(); + ranges.SetCount(numAxis); + for (int ix = 0; ix < numAxis; ++ix) { + ranges[ix].SetCount(_ranges[ix].GetCount()); + for (int d = 0; d < _ranges[ix].GetCount(); ++d) + ranges[ix][d] = _ranges[ix][d]; + } + totalVals = total; + for (int i = 0; i < data.GetCount(); ++i) + values(ptrdiff_t(i)) = data[i]; + return *this; +} + +Histogram &Histogram::Create(Upp::Array &dataAxis, bool isY) { + Clear(); + + int numAxis = dataAxis.GetCount(); + int numVals = 1; + for (int ix = 0; ix < numAxis; ++ix) + numVals *= dataAxis[ix].numVals; + valuesIdx.SetNumAxis(numAxis); + for (int ix = 0; ix < numAxis; ++ix) + valuesIdx.SetAxisDim(ix, dataAxis[ix].numVals); + values.resize(numVals); + values.setZero(); + ranges.SetCount(numAxis); + Vector delta; + delta.SetCount(numAxis); + for (int ix = 0; ix < numAxis; ++ix) { + ranges[ix].SetCount(dataAxis[ix].numVals); + delta[ix] = (dataAxis[ix].max - dataAxis[ix].min + 1)/dataAxis[ix].numVals; + for (int d = 0; d < dataAxis[ix].numVals; ++d) + ranges[ix][d] = (d + 1)*delta[ix]; + } + + totalVals = double(dataAxis[0].data.GetCount()); + Vector index; + index.SetCount(numAxis); + for (int64 i = 0; i < totalVals; ++i) { + for (int ix = 0; ix < numAxis; ++ix) { + double d = isY ? dataAxis[ix].data.y(i) : dataAxis[ix].data.x(i); + if (!IsNull(d)) { + double val = d - dataAxis[ix].min; + val = val/delta[ix]; + if (val >= dataAxis[ix].numVals) + val = dataAxis[ix].numVals - 1; + else if (val < 0) + val = 0; + ASSERT(int(val) >= 0 && int(val) < dataAxis[ix].numVals); + index[ix] = int(val); + } else + index[ix] = Null; + } + if (!IsNull(index[0])) + values(valuesIdx.GetIndex(index))++; + } + return *this; +} + +Histogram &Histogram::Create(DataSource &data, double min, double max, int numVals, bool isY) { + Clear(); + + values.resize(numVals); + values.setZero(); + ranges.SetCount(1); + ranges[0].SetCount(numVals); + double delta = (max - min)/numVals; + for (int ii = 0; ii < numVals; ++ii) + ranges[0][ii] = (ii + 1)*delta; + + int64 total = data.GetCount(); + totalVals = 0; + for (int64 i = 0; i < total; ++i) { + double d = isY ? data.y(i) : data.x(i); + if (!IsNull(d)) { + double val = (d - min)/delta; + if (val >= numVals) + val = numVals - 1; + else if (val < 0) + val = 0; + values(int(val))++; + totalVals++; + } + } + return *this; +} + +Histogram &Histogram::Normalize(double val) { + if (totalVals == val) + return *this; + + if (totalVals > 0) + values *= val/totalVals; + totalVals = val; + return *this; +} + +double Histogram::Compare(const Histogram &hist) { + ASSERT(hist.values.size() == values.size()); + double res = 0; + ptrdiff_t size = values.size(); + for(ptrdiff_t i = 0; i < size; i++) + res += min(values[i], hist.values[i]); + res /= totalVals; + return res; +} + +} \ No newline at end of file diff --git a/uppsrc/ScatterDraw/Histogram.h b/uppsrc/ScatterDraw/Histogram.h new file mode 100644 index 000000000..e216bfb88 --- /dev/null +++ b/uppsrc/ScatterDraw/Histogram.h @@ -0,0 +1,159 @@ +#ifndef _Mathlib_Histogram_h_ +#define _Mathlib_Histogram_h_ + +namespace Upp { + +class MultiDimMatrixIndex { +public: + MultiDimMatrixIndex() {}; + MultiDimMatrixIndex(int numAxis) {axisDim.SetCount(numAxis);}; + + void SetNumAxis(int numAxis) {axisDim.SetCount(numAxis);}; + void SetAxisDim(int axis, int dim) { + ASSERT(axis >= 0 && axis < axisDim.GetCount() && dim > 0); + axisDim[axis] = dim; + } + void SetAxis1D(int dimX) { + ASSERT(dimX > 0); + axisDim.SetCount(1); + axisDim[0] = dimX; + } + void SetAxis2D(int dimX, int dimY) { + ASSERT(dimX > 0 && dimY > 0); + axisDim.SetCount(2); + axisDim[0] = dimX; + axisDim[1] = dimY; + } + const Upp::Vector &GetAxisDim() {return axisDim;}; + int GetIndex(Vector &index) { + ASSERT_(index[0] >= 0 && index[0] < axisDim[0], Format("index[0]=%d", index[0])); + int ret = index[0]; + int factor = 1; + for (int ix = 1; ix <= axisDim.GetCount()-1; ++ix) { + ASSERT(index[ix] >= 0 && index[ix] < axisDim[ix]); + factor *= axisDim[ix-1]; + ret += factor*index[ix]; + } + return ret; + } + template + int GetIndex(T t, Args... args) { + Vector index; + + index << t; + AddIndex(index, args...); + + return GetIndex(index); + } + inline int GetIndex(int x, int y) { + ASSERT(IsValid(x, y)); + return x + axisDim[0]*y; + } + inline bool IsValid(int x, int y) { + return x >= 0 && x < axisDim[0] && y >= 0 && y < axisDim[1]; + } + inline int GetNumAxis() {return axisDim.GetCount();} + + void Xmlize(XmlIO xml) { + xml + ("axisDim", axisDim) + ; + } + void Jsonize(JsonIO& json) { + json + ("axisDim", axisDim) + ; + } +private: + Vector axisDim; + + template + void AddIndex(Vector &index, T t) { + index << t; + } + template + void AddIndex(Vector &index, T t, Args... args) { + index << t; + AddIndex(index, args...); + } +}; + +struct HistogramDataAxis : Moveable { + HistogramDataAxis(DataSource &data, double min, double max, int numVals) : data(data), min(min), max(max), numVals(numVals) {} + DataSource &data; + double min, max; + int numVals; +}; + +class Histogram : public DataSource, public Moveable { +public: + Histogram(); + Histogram(const Histogram &hist); + Histogram(const Nuller&) {SetNull();} + + void SetNull() {Clear();} + bool IsNullInstance() const {return ranges.IsEmpty();} + + virtual Histogram &operator=(const Histogram &hist); + void Clear(); + + Histogram &Create(DataSource &data, double min, double max, int numVals, bool isY); + Histogram &Create(Upp::Array &dataAxis, bool isY); + Histogram &Create2D(Vector > &_ranges, Vector &data, double total); + + Histogram &Normalize(double val = 1); + Histogram &Accumulative(bool _accum = true) {accumulative = _accum; return *this;} + + double Compare(const Histogram &hist); + + inline double &GetRange(int dim, int i) {return ranges[dim][i];}; + inline int64 GetCount(int dim) const {return ranges[dim].GetCount();} + inline double GetValue(int index) {return y(index);} + inline double GetValue(int c, int r) {return y(valuesIdx.GetIndex(c, r));} + inline bool IsValid(int c, int r) {return valuesIdx.IsValid(c, r);} + + inline double y(int64 id) { + ASSERT(values.size()); + if (!accumulative) + return values(ptrdiff_t(id)); + else { + double ret = 0; + for (ptrdiff_t i = 0; i <= ptrdiff_t(id); ++i) + ret += values(i); + return ret; + } + } + inline double x(int64 id) { + ASSERT(values.size()); + return id == 0 ? ranges[0][0]/2 : (ranges[0][int(id)] + ranges[0][int(id)-1])/2.; + } + inline virtual int64 GetCount() const {return !values.size() ? 0 : values.size();}; + + void Xmlize(XmlIO xml) { + xml + ("totalVals", totalVals) + ("valuesIdx", valuesIdx) + ("ranges", ranges) + ; + Upp::Xmlize(xml, values); + } + void Jsonize(JsonIO& json) { + json + ("totalVals", totalVals) + ("valuesIdx", valuesIdx) + ("ranges", ranges) + ; + Upp::Jsonize(json, values); + } +protected: + double totalVals; + bool accumulative = false; + + MultiDimMatrixIndex valuesIdx; + Eigen::VectorXd values; + Vector > ranges; +}; + +} + +#endif diff --git a/uppsrc/ScatterDraw/ScatterDraw.cpp b/uppsrc/ScatterDraw/ScatterDraw.cpp index 8acbdbe4a..ab32232b2 100644 --- a/uppsrc/ScatterDraw/ScatterDraw.cpp +++ b/uppsrc/ScatterDraw/ScatterDraw.cpp @@ -477,9 +477,9 @@ String ScatterDraw::VariableFormat(double range, double d) { Color ScatterDraw::GetNewColor(int index, int version) { Color old[20] = {LtBlue(), LtRed(), LtGreen(), Black(), LtGray(), Brown(), Blue(), Red(), Green(), Gray(), LtBlue(), LtRed(), LtGreen(), Black(), LtGray(), Brown(), Blue(), Red(), Green(), Gray()}; - // Colors from http://tools.medialab.sciences-po.fr/iwanthue/ + // Colours from http://tools.medialab.sciences-po.fr/iwanthue/ Color nwc[20] = {Color(197,127,117), Color(115,214,74), Color(205,80,212), Color(124,193,215), Color(85,82,139), - Color(63,72,41), Color(109,212,161), Color(207,72,48), Color(209,206,59), Color(194,134,55), + Color(109,212,161), Color(207,72,48), Color(209,206,59), Color(194,134,55), Color(63,72,41), Color(201,63,109), Color(193,192,158), Color(91,134,56), Color(105,48,38), Color(201,170,200), Color(86,117,119), Color(188,91,165), Color(124,120,216), Color(195,208,119), Color(79,46,75)}; if (index < 20) { @@ -1145,6 +1145,7 @@ void ScatterDraw::Show(int index, bool show) { ASSERT(!series[index].IsDeleted()); series[index].opacity = show ? 1 : 0; + labelsChanged = true; Refresh(); } diff --git a/uppsrc/ScatterDraw/ScatterDraw.h b/uppsrc/ScatterDraw/ScatterDraw.h index f2d814f3e..a09a3ecd3 100644 --- a/uppsrc/ScatterDraw/ScatterDraw.h +++ b/uppsrc/ScatterDraw/ScatterDraw.h @@ -1290,7 +1290,7 @@ bool ScatterDraw::PlotTexts(T& w, const bool boldX, bool boldY) { Upp::Index xUnits, yUnits, yUnits2; for (int i = 0; i < series.GetCount(); ++i) { const ScatterSeries &serie = series[i]; - if (serie.IsDeleted()) + if (serie.IsDeleted() || serie.opacity == 0) continue; if (serie.primaryY) { if (yLabel.IsEmpty()) { diff --git a/uppsrc/ScatterDraw/ScatterDraw.upp b/uppsrc/ScatterDraw/ScatterDraw.upp index 243f5f5a7..7392b3b44 100644 --- a/uppsrc/ScatterDraw/ScatterDraw.upp +++ b/uppsrc/ScatterDraw/ScatterDraw.upp @@ -16,6 +16,8 @@ file DrawingFunctions.cpp, DrawingFunctions.h, SeriesPlot.h, + Histogram.cpp, + Histogram.h, MarkPlot.h, Legend.cpp, Surf.cpp,