GRASS 8 Programmer's Manual 8.6.0dev(2026)-0aaa18e7c0
Loading...
Searching...
No Matches
class.c
Go to the documentation of this file.
1/* functions to classify sorted arrays of doubles and fill a vector of
2 * classbreaks */
3
4#include <stdbool.h>
5
6#include <grass/glocale.h>
7#include <grass/arraystats.h>
8
10{
11 if (G_strcasecmp(option->answer, "int") == 0)
12 return CLASS_INTERVAL;
13 if (G_strcasecmp(option->answer, "std") == 0)
14 return CLASS_STDEV;
15 if (G_strcasecmp(option->answer, "qua") == 0)
16 return CLASS_QUANT;
17 if (G_strcasecmp(option->answer, "equ") == 0)
18 return CLASS_EQUIPROB;
19 if (G_strcasecmp(option->answer, "dis") == 0)
20 return CLASS_DISCONT;
21
22 G_fatal_error(_("Unknown algorithm '%s'"), option->answer);
23}
24
25double AS_class_apply_algorithm(int algo, const double data[], int nrec,
26 int *nbreaks, double classbreaks[])
27{
28 double finfo = 0.0;
29
30 if (nrec < 1)
31 G_fatal_error(_("Cannot classify an empty set of values"));
32
33 switch (algo) {
34 case CLASS_INTERVAL:
36 break;
37 case CLASS_STDEV:
39 break;
40 case CLASS_QUANT:
42 break;
43 case CLASS_EQUIPROB:
45 break;
46 case CLASS_DISCONT:
48 break;
49 default:
50 break;
51 }
52
53 if (finfo == 0)
54 G_fatal_error(_("Classification algorithm failed"));
55
56 return finfo;
57}
58
59int AS_class_interval(const double data[], int count, int nbreaks,
60 double classbreaks[])
61{
62 double min, max;
63 double step;
64 int i = 0;
65
66 min = data[0];
67 max = data[count - 1];
68
69 step = (max - min) / (nbreaks + 1);
70
71 for (i = 0; i < nbreaks; i++)
72 classbreaks[i] = min + (step * (i + 1));
73
74 return (1);
75}
76
77double AS_class_stdev(const double data[], int count, int nbreaks,
78 double classbreaks[])
79{
80 struct GASTATS stats;
81 int i;
82 int nbclass;
83 double scale = 1.0;
84
85 AS_basic_stats(data, count, &stats);
86
87 nbclass = nbreaks + 1;
88
89 if (nbclass % 2 ==
90 1) { /* number of classes is uneven so we center middle class on mean */
91
92 /* find appropriate fraction of stdev for step */
93 i = 1;
94 while (i) {
95 if (((stats.mean + stats.stdev * scale / 2) +
96 (stats.stdev * scale * (nbclass / 2 - 1)) >
97 stats.max) ||
98 ((stats.mean - stats.stdev * scale / 2) -
99 (stats.stdev * scale * (nbclass / 2 - 1)) <
100 stats.min))
101 scale = scale / 2;
102 else
103 i = 0;
104 }
105
106 /* classbreaks below the mean */
107 for (i = 0; i < nbreaks / 2; i++)
108 classbreaks[i] = (stats.mean - stats.stdev * scale / 2) -
109 stats.stdev * scale * (nbreaks / 2 - (i + 1));
110 /* classbreaks above the mean */
111 for (; i < nbreaks; i++)
112 classbreaks[i] = (stats.mean + stats.stdev * scale / 2) +
113 stats.stdev * scale * (i - nbreaks / 2);
114 }
115 else { /* number of classes is even so mean is a classbreak */
116
117 /* decide whether to use 1*stdev or 0.5*stdev as step */
118 i = 1;
119 while (i) {
120 if (((stats.mean) + (stats.stdev * scale * (nbclass / 2 - 1)) >
121 stats.max) ||
122 ((stats.mean) - (stats.stdev * scale * (nbclass / 2 - 1)) <
123 stats.min))
124 scale = scale / 2;
125 else
126 i = 0;
127 }
128
129 /* classbreaks below the mean and on the mean */
130 for (i = 0; i <= nbreaks / 2; i++)
131 classbreaks[i] =
132 stats.mean - stats.stdev * scale * (nbreaks / 2 - i);
133 /* classbreaks above the mean */
134 for (; i < nbreaks; i++)
135 classbreaks[i] =
136 stats.mean + stats.stdev * scale * (i - nbreaks / 2);
137 }
138
139 return (scale);
140}
141
142int AS_class_quant(const double data[], int count, int nbreaks,
143 double classbreaks[])
144{
145 int i, step;
146
147 step = count / (nbreaks + 1);
148
149 for (i = 0; i < nbreaks; i++)
150 classbreaks[i] = data[step * (i + 1)];
151
152 return (1);
153}
154
155int AS_class_equiprob(const double data[], int count, int *nbreaks,
156 double classbreaks[])
157{
158 int i, j;
159 double *lequi; /*Vector of scale factors for probabilities of the normal
160 distribution */
161 struct GASTATS stats;
162 int nbclass;
163
164 nbclass = *nbreaks + 1;
165
166 lequi = G_malloc(*nbreaks * sizeof(double));
167
168 /* The following values come from the normal distribution and will be used
169 * as: classbreak[i] = (lequi[i] * stdev) + mean;
170 */
171
172 if (nbclass < 3) {
173 lequi[0] = 0;
174 }
175 else if (nbclass == 3) {
176 lequi[0] = -0.43076;
177 lequi[1] = 0.43076;
178 }
179 else if (nbclass == 4) {
180 lequi[0] = -0.6745;
181 lequi[1] = 0;
182 lequi[2] = 0.6745;
183 }
184 else if (nbclass == 5) {
185 lequi[0] = -0.8416;
186 lequi[1] = -0.2533;
187 lequi[2] = 0.2533;
188 lequi[3] = 0.8416;
189 }
190 else if (nbclass == 6) {
191 lequi[0] = -0.9676;
192 lequi[1] = -0.43076;
193 lequi[2] = 0;
194 lequi[3] = 0.43076;
195 lequi[4] = 0.9676;
196 }
197 else if (nbclass == 7) {
198 lequi[0] = -1.068;
199 lequi[1] = -0.566;
200 lequi[2] = -0.18;
201 lequi[3] = 0.18;
202 lequi[4] = 0.566;
203 lequi[5] = 1.068;
204 }
205 else if (nbclass == 8) {
206 lequi[0] = -1.1507;
207 lequi[1] = -0.6745;
208 lequi[2] = -0.3187;
209 lequi[3] = 0;
210 lequi[4] = 0.3187;
211 lequi[5] = 0.6745;
212 lequi[6] = 1.1507;
213 }
214 else if (nbclass == 9) {
215 lequi[0] = -1.2208;
216 lequi[1] = -0.7648;
217 lequi[2] = -0.4385;
218 lequi[3] = -0.1397;
219 lequi[4] = 0.1397;
220 lequi[5] = 0.4385;
221 lequi[6] = 0.7648;
222 lequi[7] = 1.2208;
223 }
224 else if (nbclass == 10) {
225 lequi[0] = -1.28155;
226 lequi[1] = -0.84162;
227 lequi[2] = -0.5244;
228 lequi[3] = -0.25335;
229 lequi[4] = 0;
230 lequi[5] = 0.25335;
231 lequi[6] = 0.5244;
232 lequi[7] = 0.84162;
233 lequi[8] = 1.28155;
234 }
235 else {
237 _("Equiprobable classbreaks currently limited to 10 classes"));
238 }
239
240 AS_basic_stats(data, count, &stats);
241
242 /* Check if any of the classbreaks would fall outside of the range min-max
243 */
244 j = 0;
245 for (i = 0; i < *nbreaks; i++) {
246 if ((lequi[i] * stats.stdev + stats.mean) >= stats.min &&
247 (lequi[i] * stats.stdev) + stats.mean <= stats.max) {
248 j++;
249 }
250 }
251
252 if (j < (*nbreaks)) {
253 G_warning(
254 _("There are classbreaks outside the range min-max. Number of "
255 "classes reduced to %i, but using probabilities for %i classes."),
256 j + 1, *nbreaks + 1);
257 for (i = 0; i < j; i++)
258 classbreaks[i] = 0.0;
259 }
260
261 j = 0;
262 for (i = 0; i < *nbreaks; i++) {
263 if ((lequi[i] * stats.stdev + stats.mean) >= stats.min &&
264 (lequi[i] * stats.stdev) + stats.mean <= stats.max) {
265 classbreaks[j] = lequi[i] * stats.stdev + stats.mean;
266 j++;
267 }
268 }
269 *nbreaks = j;
270
271 G_free(lequi);
272 return (1);
273}
274
275double AS_class_discont(const double data[], int count, int nbreaks,
276 double classbreaks[])
277{
278 int i, j;
279 double chi2 = 1000.0;
280
281 /* get the number of values */
282 int n = count;
283
284 int nbclass = nbreaks + 1;
285
286 int *num = G_calloc((nbclass + 2), sizeof(int));
287 int *no = G_calloc((nbclass + 1), sizeof(int));
288 double *zz = G_malloc((nbclass + 1) * sizeof(double));
289 double *xn = G_malloc((n + 1) * sizeof(double));
290 double *co = G_malloc((nbclass + 1) * sizeof(double));
291
292 /* We copy the array of values to x, in order to be able
293 to standardize it */
294 double *x = G_malloc((n + 1) * sizeof(double));
295 x[0] = 0.0;
296 xn[0] = 0.0;
297
298 double min = data[0];
299 double max = data[count - 1];
300 for (i = 1; i <= n; i++)
301 x[i] = data[i - 1];
302
303 double rangemax = max - min;
304 double rangemin = rangemax;
305
306 for (i = 2; i <= n; i++) {
307 if (x[i] != x[i - 1] && x[i] - x[i - 1] < rangemin)
308 rangemin = x[i] - x[i - 1]; /* rangemin = minimal distance */
309 }
310
311 /* STANDARDIZATION
312 * and creation of the number vector (xn) */
313
314 for (i = 1; i <= n; i++) {
315 x[i] = (x[i] - min) / rangemax;
316 xn[i] = i / (double)n;
317 }
318 double xlim = rangemin / rangemax;
319 rangemin = rangemin / 2.0;
320 /* Searching for the limits */
321 num[1] = n;
322
323 /* Loop through possible solutions */
324 for (i = 1; i <= nbclass; i++) {
325 double dmax = 0.0;
326 int nmax = 0;
327 int nf = 0; /* End number */
328
329 /* Loop through classes */
330 for (j = 1; j <= i; j++) {
331 double a = 0.0, b = 0.0, c = 0.0, d = 0.0;
332 int nd = nf; /* Start number */
333
334 nf = num[j];
335 co[j] = 10e37;
336 AS_eqdrt(x, xn, nd, nf, &a, &b, &c);
337 double den = sqrt(pow(b, 2) + 1.0);
338 nd++;
339 /* Loop through observations */
340 for (int k = nd; k <= nf; k++) {
341 if (fabs(c) >= GRASS_EPSILON)
342 d = fabs(x[k] - c);
343 else
344 d = fabs((-1.0 * b * x[k]) + xn[k] - a) / den;
345
346 if (x[k] - x[nd] < xlim)
347 continue;
348 if (x[nf] - x[k] < xlim)
349 continue;
350 if (d <= dmax)
351 continue;
352 dmax = d;
353 nmax = k;
354 }
355 nd--;
356 if (fabs(x[nf] - x[nd]) > GRASS_EPSILON)
357 co[j] = (xn[nf] - xn[nd]) / (x[nf] - x[nd]);
358 }
359 for (j = 1; j <= i; j++) {
360 no[j] = num[j];
361 zz[j] = x[num[j]] * rangemax + min;
362 if (j == i)
363 continue;
364 if (co[j] > co[j + 1]) {
365 zz[j] = zz[j] + rangemin;
366 continue;
367 }
368 else {
369 zz[j] = zz[j] - rangemin;
370 no[j] = no[j] - 1;
371 }
372 }
373 int im = i - 1;
374 if (im != 0) {
375 for (j = 1; j <= im; j++) {
376 int ji = i + 1 - j;
377 no[ji] -= no[ji - 1];
378 }
379 }
380 if (nmax == 0) {
381 break;
382 }
383
384 int jj = 0;
385 int nff = i + 2;
386 bool do_reset = true;
387 for (j = 1; j <= i; j++) {
388 jj = nff - j;
389 if (num[jj - 1] < nmax) {
390 num[jj] = nmax;
391 do_reset = false;
392 break;
393 }
394 num[jj] = num[jj - 1];
395 }
396 if (do_reset) {
397 num[1] = nmax;
398 jj = 1;
399 }
400 int no1 = (int)((xn[num[jj]] - xn[num[jj - 1]]) * n);
401 int no2 = (int)((xn[num[jj + 1]] - xn[num[jj]]) * n);
402 double f = (xn[num[jj + 1]] - xn[num[jj - 1]]) /
403 (x[num[jj + 1]] - x[num[jj - 1]]);
404 f *= n;
405 double xt1 = (x[num[jj]] - x[num[jj - 1]]) * f;
406 double xt2 = (x[num[jj + 1]] - x[num[jj]]) * f;
407 if (fabs(xt1 * xt2) <= GRASS_EPSILON) {
408 if (fabs(xt2) > GRASS_EPSILON) {
409 xt2 = rangemin / 2.0 / rangemax * f;
410 xt1 = xt1 - xt2;
411 }
412 else {
413 xt1 = rangemin / 2.0 / rangemax * f;
414 xt2 = xt2 - xt1;
415 }
416 }
417
418 /* calculate chi-square to indicate statistical significance of new
419 * class, i.e. how probable would it be that the new class could be the
420 * result of purely random choice */
421 double ch = pow((double)((no1 - no2) - (xt1 - xt2)), 2) / (xt1 + xt2);
422 if (chi2 > ch)
423 chi2 = ch;
424 }
425
426 /* Fill up classbreaks of i <nbclass classes */
427 for (j = 1; j < nbclass; j++)
428 classbreaks[j - 1] = zz[j];
429
430 G_free(co);
431 G_free(no);
432 G_free(num);
433 G_free(x);
434 G_free(xn);
435 G_free(zz);
436
437 return (chi2);
438}
439
440int AS_class_frequencies(const double data[], int count, int nbreaks,
441 double classbreaks[], int frequencies[])
442{
443 int i, j;
444
445 /* min = data[0];
446 max = data[count - 1]; */
447 /* count cases in all classes, except for last class */
448 i = 0;
449 for (j = 0; j < nbreaks; j++) {
450 while (data[i] <= classbreaks[j]) {
451 frequencies[j]++;
452 i++;
453 }
454 }
455
456 /*Now count cases in last class */
457 for (; i < count; i++) {
459 }
460
461 return (1);
462}
#define CLASS_EQUIPROB
Definition arraystats.h:27
#define CLASS_QUANT
Definition arraystats.h:26
#define CLASS_INTERVAL
Definition arraystats.h:24
#define CLASS_DISCONT
Definition arraystats.h:28
#define CLASS_STDEV
Definition arraystats.h:25
double AS_class_discont(const double data[], int count, int nbreaks, double classbreaks[])
Definition class.c:275
double AS_class_apply_algorithm(int algo, const double data[], int nrec, int *nbreaks, double classbreaks[])
Definition class.c:25
int AS_class_quant(const double data[], int count, int nbreaks, double classbreaks[])
Definition class.c:142
int AS_class_frequencies(const double data[], int count, int nbreaks, double classbreaks[], int frequencies[])
Definition class.c:440
int AS_class_equiprob(const double data[], int count, int *nbreaks, double classbreaks[])
Definition class.c:155
int AS_class_interval(const double data[], int count, int nbreaks, double classbreaks[])
Definition class.c:59
int AS_option_to_algorithm(const struct Option *option)
Definition class.c:9
double AS_class_stdev(const double data[], int count, int nbreaks, double classbreaks[])
Definition class.c:77
void AS_eqdrt(double[], double[], int, int, double *, double *, double *)
Definition basic.c:39
void AS_basic_stats(const double[], int, struct GASTATS *)
Definition basic.c:7
void G_free(void *)
Free allocated memory.
Definition gis/alloc.c:147
#define G_calloc(m, n)
Definition defs/gis.h:140
void void void void G_fatal_error(const char *,...) __attribute__((format(printf
void G_warning(const char *,...) __attribute__((format(printf
#define G_malloc(n)
Definition defs/gis.h:139
int int G_strcasecmp(const char *, const char *)
String compare ignoring case (upper or lower)
Definition strings.c:47
#define min(x, y)
Definition draw2.c:29
#define max(x, y)
Definition draw2.c:30
#define GRASS_EPSILON
Definition gis.h:178
#define _(str)
Definition glocale.h:10
int count
double b
Definition r_raster.c:39
double mean
Definition arraystats.h:18
double max
Definition arraystats.h:14
double stdev
Definition arraystats.h:21
double min
Definition arraystats.h:13
double count
Definition arraystats.h:12
Structure that stores option information.
Definition gis.h:563
#define x