2 * analysis-histogram.c:
4 * This is a complete reimplementation of the histogram tool in 2008
7 * Andreas J. Guelzow <aguelzow@pyrshep.ca>
9 * (C) Copyright 2008 by Andreas J. Guelzow <aguelzow@pyrshep.ca>
12 * This program is free software; you can redistribute it and/or modify
13 * it under the terms of the GNU General Public License as published by
14 * the Free Software Foundation; either version 2 of the License, or
15 * (at your option) any later version.
17 * This program is distributed in the hope that it will be useful,
18 * but WITHOUT ANY WARRANTY; without even the implied warranty of
19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 * GNU General Public License for more details.
22 * You should have received a copy of the GNU General Public License
23 * along with this program; if not, see <https://www.gnu.org/licenses/>.
26 #include <gnumeric-config.h>
27 #include <glib/gi18n-lib.h>
29 #include <tools/analysis-histogram.h>
30 #include <tools/analysis-tools.h>
36 #include <sheet-object-graph.h>
37 #include <goffice/goffice.h>
40 static GnmExpr
const *
41 make_hist_expr (analysis_tools_data_histogram_t
*info
,
42 int col
, GnmValue
*val
,
43 gboolean fromminf
, gboolean topinf
,
44 data_analysis_output_t
*dao
)
47 GnmExpr
const *expr_data
;
48 GnmExpr
const *expr_if_to
, *expr_if_from
;
50 GnmFunc
*fd_if
= gnm_func_lookup_or_add_placeholder ("IF");
51 GnmFunc
*fd_sum
= gnm_func_lookup_or_add_placeholder ("SUM");
52 GnmFunc
*fd_count
= info
->percentage
?
53 gnm_func_lookup_or_add_placeholder (info
->only_numbers
? "COUNT" : "COUNTA") : NULL
;
54 GnmFunc
*fd_isnumber
= gnm_func_lookup_or_add_placeholder (info
->only_numbers
? "ISNUMBER" : "ISBLANK");
55 gint to_col
= (info
->cumulative
) ? 0 : 1;
57 if (info
->bin_type
& bintype_no_inf_upper
) {
58 from
= GNM_EXPR_OP_LT
;
61 from
= GNM_EXPR_OP_LTE
;
65 expr_data
= gnm_expr_new_constant (value_dup (val
));
67 expr_if_to
= gnm_expr_new_constant (value_new_int (1));
69 expr_if_to
= gnm_expr_new_funcall3
72 (gnm_expr_copy (expr_data
),
73 to
, make_cellref (- (col
-to_col
), 0)),
74 gnm_expr_new_constant (value_new_int (0)),
75 gnm_expr_new_constant (value_new_int (1)));
80 GnmExpr
const *one
= gnm_expr_new_constant (value_new_int (1));
84 expr_if_from
= gnm_expr_new_funcall3
87 (gnm_expr_copy (expr_data
),
88 from
, make_cellref (- col
, 0)),
89 gnm_expr_new_constant (value_new_int (0)),
91 expr
= gnm_expr_new_binary (expr_if_from
,
96 if (info
->only_numbers
)
97 expr
= gnm_expr_new_binary (expr
,
100 (fd_if
,gnm_expr_new_funcall1
101 (fd_isnumber
, gnm_expr_copy (expr_data
)),
102 gnm_expr_new_constant (value_new_int (1)),
103 gnm_expr_new_constant (value_new_int (0))));
105 expr
= gnm_expr_new_binary (expr
,
107 gnm_expr_new_funcall3
108 (fd_if
,gnm_expr_new_funcall1
109 (fd_isnumber
, gnm_expr_copy (expr_data
)),
110 gnm_expr_new_constant (value_new_int (0)),
111 gnm_expr_new_constant (value_new_int (1))));
114 expr
= gnm_expr_new_funcall1 (fd_sum
, expr
);
116 if (info
->percentage
)
117 expr
= gnm_expr_new_binary (expr
,
119 gnm_expr_new_funcall1
123 gnm_expr_free (expr_data
);
129 analysis_tool_histogram_engine_run (data_analysis_output_t
*dao
,
130 analysis_tools_data_histogram_t
*info
)
133 gint i
, i_limit
, i_start
, i_end
, col
;
135 gint to_col
= (info
->cumulative
) ? 0 : 1;
137 GnmExpr
const *expr_bin
= NULL
;
140 GnmFunc
*fd_index
= NULL
;
144 fd_small
= gnm_func_lookup_or_add_placeholder ("SMALL");
145 gnm_func_inc_usage (fd_small
);
147 if (info
->base
.labels
) {
148 fd_index
= gnm_func_lookup_or_add_placeholder ("INDEX");
149 gnm_func_inc_usage (fd_index
);
155 dao_set_italic (dao
, 0, 0, 0, 0);
156 dao_set_cell (dao
, 0, 0, _("Histogram"));
158 /* Setting up the bins */
160 if (info
->predetermined
) {
161 range_init_value (&range
, info
->bin
);
162 i_limit
= range_height (&range
) * range_width (&range
);
168 if (info
->bin_type
& bintype_p_inf_lower
)
170 if (info
->bin_type
& bintype_m_inf_lower
)
172 dao_set_format (dao
, to_col
, 1, to_col
, 1, "\"\";\"\"");
173 format
= (info
->bin_type
& bintype_no_inf_upper
) ?
174 /* translator note: only translate the */
175 /* "to below" and "up to" exclusive of */
176 /* the quotation marks: */
177 _("\"to below\" * General") : _("\"up to\" * General");
178 dao_set_format (dao
, to_col
, 2, to_col
, i_end
, format
);
180 if (info
->bin_type
& bintype_m_inf_lower
) {
181 dao_set_cell_value (dao
, to_col
, 1, value_new_float (-GNM_MAX
));
186 if (info
->predetermined
) {
187 expr_bin
= gnm_expr_new_constant (info
->bin
);
188 for (i
= 0; i
< i_limit
; i
++)
189 dao_set_cell_expr (dao
, to_col
, i_start
+ i
,
190 gnm_expr_new_funcall2 (fd_small
,
191 gnm_expr_copy (expr_bin
),
192 gnm_expr_new_constant
193 (value_new_int (i
+ 1))));
195 GnmValue
*val
= value_dup (info
->base
.input
->data
);
196 GnmExpr
const *expr_min
;
197 GnmExpr
const *expr_max
;
199 if (info
->base
.labels
)
200 switch (info
->base
.group_by
) {
202 val
->v_range
.cell
.a
.col
++;
205 val
->v_range
.cell
.a
.row
++;
210 dao_set_cell_float (dao
, to_col
, i_start
, info
->min
);
214 fd_min
= gnm_func_lookup_or_add_placeholder ("MIN");
215 gnm_func_inc_usage (fd_min
);
216 dao_set_cell_expr (dao
, to_col
, i_start
,
217 gnm_expr_new_funcall1
219 gnm_expr_new_constant (value_dup (val
))));
220 gnm_func_dec_usage (fd_min
);
224 dao_set_cell_float (dao
, to_col
, i_start
+ i_limit
- 1, info
->max
);
228 fd_max
= gnm_func_lookup_or_add_placeholder ("MAX");
229 gnm_func_inc_usage (fd_max
);
230 dao_set_cell_expr (dao
, to_col
, i_start
+ i_limit
- 1,
231 gnm_expr_new_funcall1
233 gnm_expr_new_constant (value_dup (val
))));
234 gnm_func_dec_usage (fd_max
);
239 expr_min
= dao_get_cellref (dao
, to_col
, i_start
);
240 expr_max
= dao_get_cellref (dao
, to_col
, i_start
+ i_limit
- 1);
242 for (i
= 1; i
< i_limit
- 1; i
++)
243 dao_set_cell_expr (dao
, to_col
, i_start
+ i
,
244 gnm_expr_new_binary (gnm_expr_copy (expr_min
),
247 (gnm_expr_new_constant (value_new_int (i
)),
251 (gnm_expr_copy (expr_max
),
253 gnm_expr_copy (expr_min
)),
255 gnm_expr_new_constant (value_new_int (info
->n
- 1))))));
257 gnm_expr_free (expr_min
);
258 gnm_expr_free (expr_max
);
261 if (info
->bin_type
& bintype_p_inf_lower
) {
262 dao_set_format (dao
, to_col
, i_end
, to_col
, i_end
,
263 /* translator note: only translate the */
264 /* "to" and "\xe2\x88\x9e" exclusive of */
265 /* the quotation marks: */
266 _("\"to\" * \"\xe2\x88\x9e\""));
267 dao_set_cell_value (dao
, to_col
, i_end
, value_new_float (GNM_MAX
));
270 /* format the lower end of the bins */
272 if (!info
->cumulative
) {
273 GnmExpr
const *expr_cr
= make_cellref (1,-1);
275 format
= (info
->bin_type
& bintype_no_inf_upper
) ?
276 /* translator note: only translate the */
277 /* "from" and "above" exclusive of */
278 /* the quotation marks: */
279 _("\"from\" * General") : _("\"above\" * General");
280 dao_set_format (dao
, 0, 2, 0, i_end
, format
);
281 if (info
->bin_type
& bintype_m_inf_lower
)
282 dao_set_format (dao
, 0, 2, 0, 2,
283 /* translator note: only translate the */
284 /* "from" and "\xe2\x88\x92\xe2\x88\x9e" exclusive of */
285 /* the quotation marks: */
286 _("\"from\" * \"\xe2\x88\x92\xe2\x88\x9e\";"
287 "\"from\" * \"\xe2\x88\x92\xe2\x88\x9e\""));
288 for (i
= 2; i
<= i_end
; i
++)
289 dao_set_cell_expr (dao
, 0, i
, gnm_expr_copy (expr_cr
));
291 gnm_expr_free (expr_cr
);
294 /* insert formulas for histogram values */
296 for (l
= info
->base
.input
, col
= to_col
+ 1; l
; col
++, l
= l
->next
) {
297 GnmValue
*val
= l
->data
;
298 GnmValue
*val_c
= NULL
;
300 dao_set_italic (dao
, col
, 1, col
, 1);
301 if (info
->base
.labels
) {
302 val_c
= value_dup (val
);
303 switch (info
->base
.group_by
) {
305 val
->v_range
.cell
.a
.col
++;
308 val
->v_range
.cell
.a
.row
++;
311 dao_set_cell_expr (dao
, col
, 1,
312 gnm_expr_new_funcall1 (fd_index
,
313 gnm_expr_new_constant (val_c
)));
317 switch (info
->base
.group_by
) {
319 format
= _("Row %d");
322 format
= _("Column %d");
325 format
= _("Area %d");
328 dao_set_cell_printf (dao
, col
, 1, format
, col
- to_col
);
331 if (info
->percentage
)
332 dao_set_format (dao
, col
, 2, col
, i_end
, "0.0%");
334 for (i
= 2; i
<= i_end
; i
++) {
335 gboolean fromminf
= (i
== 2) &&
336 (info
->bin_type
& bintype_m_inf_lower
);
337 gboolean topinf
= (i
== i_end
) &&
338 (info
->bin_type
& bintype_p_inf_lower
);
339 dao_set_cell_array_expr
341 make_hist_expr (info
, col
, val
,
342 fromminf
, topinf
, dao
));
347 if (expr_bin
!= NULL
)
348 gnm_expr_free (expr_bin
);
350 gnm_func_dec_usage (fd_small
);
351 if (fd_index
!= NULL
)
352 gnm_func_dec_usage (fd_index
);
354 /* Create Chart if requested */
355 if (info
->chart
!= NO_CHART
) {
361 gint limits_start
, limits_end
, values_start
, values_end
;
366 graph
= g_object_new (GOG_TYPE_GRAPH
, NULL
);
367 chart
= GOG_CHART (gog_object_add_by_name (
368 GOG_OBJECT (graph
), "Chart", NULL
));
370 if (info
->chart
== HISTOGRAM_CHART
) {
371 plot
= gog_plot_new_by_name ("GogHistogramPlot");
372 limits_start
= i_start
;
373 limits_end
= i_start
+ i_limit
- 1;
374 values_start
= i_start
+ 1;
375 values_end
= i_start
+ i_limit
- 1;
377 plot
= gog_plot_new_by_name ("GogBarColPlot");
382 if (info
->chart
== BAR_CHART
)
383 go_object_toggle (plot
, "horizontal");
386 gog_object_add_by_name (GOG_OBJECT (chart
),
387 "Plot", GOG_OBJECT (plot
));
389 limits
= dao_go_data_vector (dao
, to_col
, limits_start
,
392 for (ct
= 1; ct
< (col
- to_col
); ct
++) {
393 g_object_ref (limits
);
394 values
= dao_go_data_vector (dao
, to_col
+ ct
, values_start
,
395 to_col
+ ct
, values_end
);
397 series
= gog_plot_new_series (plot
);
398 gog_series_set_dim (series
, 0, limits
, NULL
);
399 gog_series_set_dim (series
, 1, values
, NULL
);
401 g_object_unref (limits
);
403 if (info
->chart
== HISTOGRAM_CHART
) {
405 axis
= gog_object_get_child_by_name (GOG_OBJECT (chart
), "X-Axis");
406 go_object_set_property (G_OBJECT (axis
), "assigned-format-string-XL",
407 "X-Axis Format", "0.0EE0",
413 so
= sheet_object_graph_new (graph
);
414 g_object_unref (graph
);
416 dao_set_sheet_object (dao
, 0, 1, so
);
419 dao_redraw_respan (dao
);
426 calc_length (GnmValue
*bin
)
428 g_return_val_if_fail (bin
!= NULL
, 0);
429 g_return_val_if_fail (VALUE_IS_CELLRANGE (bin
), 0);
431 return ((bin
->v_range
.cell
.b
.col
- bin
->v_range
.cell
.a
.col
+ 1) *
432 (bin
->v_range
.cell
.b
.row
- bin
->v_range
.cell
.a
.row
+ 1));
436 analysis_tool_histogram_engine (G_GNUC_UNUSED GOCmdContext
*gcc
, data_analysis_output_t
*dao
, gpointer specs
,
437 analysis_tool_engine_t selector
, gpointer result
)
439 analysis_tools_data_histogram_t
*info
= specs
;
442 case TOOL_ENGINE_UPDATE_DESCRIPTOR
:
443 return (dao_command_descriptor (dao
, _("Histogram (%s)"), result
)
445 case TOOL_ENGINE_UPDATE_DAO
:
449 prepare_input_range (&info
->base
.input
, info
->base
.group_by
);
451 i
= 1 + ((info
->predetermined
) ? calc_length (info
->bin
) : info
->n
);
452 if (info
->bin_type
& bintype_p_inf_lower
)
454 if (info
->bin_type
& bintype_m_inf_lower
)
457 j
= g_slist_length (info
->base
.input
) + ((info
->cumulative
) ? 1 : 2);
459 dao_adjust (dao
, j
, i
);
463 case TOOL_ENGINE_CLEAN_UP
:
464 return analysis_tool_generic_clean (specs
);
465 case TOOL_ENGINE_LAST_VALIDITY_CHECK
:
467 case TOOL_ENGINE_PREPARE_OUTPUT_RANGE
:
468 dao_prepare_output (NULL
, dao
, _("Histogram"));
470 case TOOL_ENGINE_FORMAT_OUTPUT_RANGE
:
471 return dao_format_output (dao
, _("Histogram"));
472 case TOOL_ENGINE_PERFORM_CALC
:
474 return analysis_tool_histogram_engine_run (dao
, specs
);