aboutsummaryrefslogtreecommitdiffstats
path: root/stats/statsvariables.h
blob: 06fb90f2ad472f23c14b88554b52f9133739e1f7 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
// SPDX-License-Identifier: GPL-2.0
// Variables displayed by the statistic widgets. There are three
// kinds of variables:
// 1) Discrete variables can only adopt discrete values.
//    Examples are dive-type or dive buddy.
//    Note that for example dive buddy means that a dive can have
//    multiple values.
// 2) Continuous variables have a notion of a linear distance and can be
//    plotted on a linear axis.
//    An Example is the dive-date.
// 3) Numeric variables are continuous variables that support operations
//    such as averaging.
#ifndef STATS_TYPES_H
#define STATS_TYPES_H

#include <vector>
#include <memory>
#include <QString>
#include <QObject>

struct dive;

// Operations that can be performed on numeric variables
enum class StatsOperation : int {
	Median = 0,
	Mean,
	TimeWeightedMean,
	Sum,
	Invalid
};

// Results of the above operations
struct StatsOperationResults {
	int count;
	double median;
	double mean;
	double timeWeightedMean;
	double sum;
	StatsOperationResults(); // Initialize to invalid (e.g. no dives)
	bool isValid() const;
	double get(StatsOperation op) const;
};

// For median and quartiles.
struct StatsQuartiles {
	double min;
	double q1, q2, q3;
	double max;
	bool isValid() const;
};

struct StatsBin {
	virtual ~StatsBin();
	virtual bool operator<(StatsBin &) const = 0;
	virtual bool operator==(StatsBin &) const = 0;
	bool operator!=(StatsBin &b) const { return !(*this == b); }
};

using StatsBinPtr = std::unique_ptr<StatsBin>;

// A value and a dive
struct StatsValue {
	double v;
	dive *d;
};

// A bin and an arbitrarily associated value, e.g. a count or a list of dives.
template<typename T>
struct StatsBinValue {
	StatsBinPtr bin;
	T value;
};
using StatsBinDives = StatsBinValue<std::vector<dive *>>;
using StatsBinValues = StatsBinValue<std::vector<StatsValue>>;
using StatsBinCount = StatsBinValue<int>;
using StatsBinQuartiles = StatsBinValue<StatsQuartiles>;
using StatsBinOp = StatsBinValue<StatsOperationResults>;

struct StatsBinner {
	virtual ~StatsBinner();
	virtual QString name() const; // Only needed if there are multiple binners for a variable
	virtual QString unitSymbol() const; // For numeric variables - by default returns empty string

	// The binning functions have a parameter "fill_empty". If true, missing
	// bins in the range will be filled with empty bins. This only works for continuous variables.
	virtual std::vector<StatsBinDives> bin_dives(const std::vector<dive *> &dives, bool fill_empty) const = 0;
	virtual std::vector<StatsBinCount> count_dives(const std::vector<dive *> &dives, bool fill_empty) const = 0;

	// Note: these functions will crash with an exception if passed incompatible bins!
	virtual QString format(const StatsBin &bin) const = 0;
	QString formatWithUnit(const StatsBin &bin) const;
	virtual QString formatLowerBound(const StatsBin &bin) const; // Only for continuous variables
	virtual QString formatUpperBound(const StatsBin &bin) const; // Only for continuous variables
	virtual double lowerBoundToFloat(const StatsBin &bin) const; // Only for continuous variables
	virtual double upperBoundToFloat(const StatsBin &bin) const; // Only for continuous variables
	virtual bool preferBin(const StatsBin &bin) const; // Prefer to show this bins tick if bins are omitted. Default to true.

	// Only for continuous and numeric variables
	// Note: this will crash with an exception if passed incompatible bins!
	virtual std::vector<StatsBinPtr> bins_between(const StatsBin &bin1, const StatsBin &bin2) const;
};

// A scatter item is two values and a dive
struct StatsScatterItem {
	double x, y;
	dive *d;
};

struct StatsVariable {
	enum class Type {
		Discrete,
		Continuous,
		Numeric
	};

	virtual ~StatsVariable();
	virtual Type type() const = 0;
	virtual QString name() const = 0;
	virtual QString unitSymbol() const; // For numeric variables - by default returns empty string
	virtual int decimals() const; // For numeric variables: numbers of decimals to display on axes. Defaults to 0.
	virtual std::vector<const StatsBinner *> binners() const = 0; // Note: may depend on current locale!
	virtual QString diveCategories(const dive *d) const; // Only for discrete variables
	std::vector<StatsBinQuartiles> bin_quartiles(const StatsBinner &binner, const std::vector<dive *> &dives, bool fill_empty) const;
	std::vector<StatsBinOp> bin_operations(const StatsBinner &binner, const std::vector<dive *> &dives, bool fill_empty) const;
	std::vector<StatsBinValues> bin_values(const StatsBinner &binner, const std::vector<dive *> &dives, bool fill_empty) const;
	const StatsBinner *getBinner(int idx) const; // Handles out of bounds gracefully (returns first binner)
	QString nameWithUnit() const;
	QString nameWithBinnerUnit(const StatsBinner &) const;
	virtual std::vector<StatsOperation> supportedOperations() const; // Only for numeric variables
	QStringList supportedOperationNames() const; // Only for numeric variables
	StatsOperation idxToOperation(int idx) const;
	static QString operationName(StatsOperation);
	double mean(const std::vector<dive *> &dives) const; // Returns NaN for empty list
	static StatsQuartiles quartiles(const std::vector<StatsValue> &values); // Returns invalid quartiles for empty list
	StatsQuartiles quartiles(const std::vector<dive *> &dives) const; // Only for numeric variables
	std::vector<StatsValue> values(const std::vector<dive *> &dives) const; // Only for numeric variables
	QString valueWithUnit(const dive *d) const; // Only for numeric variables
	std::vector<StatsScatterItem> scatter(const StatsVariable &t2, const std::vector<dive *> &dives) const;
private:
	virtual double toFloat(const struct dive *d) const; // For numeric variables - if dive doesn't have that value, returns NaN
	StatsOperationResults applyOperations(const std::vector<dive *> &dives) const;
};

extern const std::vector<const StatsVariable *> stats_variables;

// Helper function for date-based variables
extern double date_to_double(int year, int month, int day);

#endif