You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
- Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
- 02111-1307, USA. */
+ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ 02110-1301, USA. */
#include "chart.h"
#include <math.h>
+#include <assert.h>
+#include "misc.h"
-/* Draw a box-and-whiskers plot
-*/
-
-struct data_stats
-{
- double ptile0 ;
- double ptile25 ;
- double median ;
- double ptile75 ;
-
- double ptile100;
-
- double outlier ;
-};
-
-
-const struct data_stats stats1 = {
- 40,
- 45,
- 54,
- 60,
- 70,
-
- 33
-};
-
-const struct data_stats stats2 = {
- 30,
- 40,
- 45,
- 54,
- 60,
-
-
- 72
-};
-
-
-
-
-
-static const double y_min = 25;
-static const double y_max = 75;
-static const double y_tick = 10;
+#include "factor_stats.h"
+/* Draw a box-and-whiskers plot
+*/
-#define min(A,B) ((A>B)?B:A)
-
-
-void draw_box_and_whiskers(struct chart *ch,
- double box_centre, const struct data_stats *s,
- const char *name);
-
+/* Draw an outlier on the plot CH
+ * at CENTRELINE
+ * The outlier is in (*wvp)[idx]
+ * If EXTREME is non zero, then consider it to be an extreme
+ * value
+ */
+void
+draw_outlier(struct chart *ch, double centreline,
+ struct weighted_value **wvp,
+ int idx,
+ short extreme);
-static double ordinate_scale;
-void
-draw_box_whisker_chart(struct chart *ch, const char *title)
+void
+draw_outlier(struct chart *ch, double centreline,
+ struct weighted_value **wvp,
+ int idx,
+ short extreme
+ )
{
- double d;
+ char label[10];
- ordinate_scale = fabs(ch->data_top - ch->data_bottom) / fabs(y_max - y_min) ;
+#define MARKER_CIRCLE 4
+#define MARKER_STAR 3
+ pl_fmarker_r(ch->lp,
+ centreline,
+ ch->data_bottom +
+ (wvp[idx]->v.f - ch->y_min ) * ch->ordinate_scale,
+ extreme?MARKER_STAR:MARKER_CIRCLE,
+ 20);
- chart_write_title(ch, title);
+ pl_moverel_r(ch->lp, 10,0);
+ snprintf(label, 10, "%d", wvp[idx]->case_nos->num);
+ pl_alabel_r(ch->lp, 'l', 'c', label);
- /* Move to data bottom-left */
- pl_move_r(ch->lp,
- ch->data_left, ch->data_bottom);
-
- for ( d = y_min; d <= y_max ; d += y_tick )
- {
- draw_tick (ch, TICK_ORDINATE, (d - y_min ) * ordinate_scale, "%g", d);
- }
+}
- draw_box_and_whiskers(ch,
- ch->data_left + 1.0/4.0 * (ch->data_right - ch->data_left) ,
- &stats1,"Stats1"
- );
- draw_box_and_whiskers(ch,
- ch->data_left + 3.0/4.0 * (ch->data_right - ch->data_left),
- &stats2,"Stats2"
- );
+void
+boxplot_draw_boxplot(struct chart *ch,
+ double box_centre,
+ double box_width,
+ struct metrics *m,
+ const char *name)
+{
+ double whisker[2];
+ int i;
+ assert(m);
-}
+ const double *hinge = m->hinge;
+ struct weighted_value **wvp = m->wvp;
+ const int n_data = m->n_data;
-void
-draw_box_and_whiskers(struct chart *ch,
- double box_centre, const struct data_stats *s,
- const char *name)
-{
+ const double step = (hinge[2] - hinge[0]) * 1.5;
- const double box_width = (ch->data_right - ch->data_left) / 4.0;
const double box_left = box_centre - box_width / 2.0;
const double box_bottom =
- ch->data_bottom + ( s->ptile25 - y_min ) * ordinate_scale;
+ ch->data_bottom + ( hinge[0] - ch->y_min ) * ch->ordinate_scale;
const double box_top =
- ch->data_bottom + ( s->ptile75 - y_min ) * ordinate_scale;
+ ch->data_bottom + ( hinge[2] - ch->y_min ) * ch->ordinate_scale;
+ /* Can't really draw a boxplot if there's no data */
+ if ( n_data == 0 )
+ return ;
- const double iq_range = s->ptile75 - s->ptile25;
+ whisker[1] = hinge[2];
+ whisker[0] = wvp[0]->v.f;
+ for ( i = 0 ; i < n_data ; ++i )
+ {
+ if ( hinge[2] + step > wvp[i]->v.f)
+ whisker[1] = wvp[i]->v.f;
+
+ if ( hinge[0] - step > wvp[i]->v.f)
+ whisker[0] = wvp[i]->v.f;
+
+ }
+
+
const double bottom_whisker =
- ch->data_bottom + (min(s->ptile0,s->ptile25 + iq_range*1.5) - y_min ) *
- ordinate_scale;
+ ch->data_bottom + ( whisker[0] - ch->y_min ) * ch->ordinate_scale;
+
+ const double top_whisker =
+ ch->data_bottom + ( whisker[1] - ch->y_min ) * ch->ordinate_scale;
- const double top_whisker =
- ch->data_bottom + (min(s->ptile100,s->ptile75 + iq_range*1.5) - y_min ) *
- ordinate_scale;
pl_savestate_r(ch->lp);
pl_linewidth_r(ch->lp,5);
pl_fline_r(ch->lp,
box_left,
- ch->data_bottom + ( s->median - y_min ) * ordinate_scale,
+ ch->data_bottom + ( hinge[1] - ch->y_min ) * ch->ordinate_scale,
box_right,
- ch->data_bottom + ( s->median - y_min ) * ordinate_scale);
+ ch->data_bottom + ( hinge[1] - ch->y_min ) * ch->ordinate_scale);
pl_restorestate_r(ch->lp);
top_whisker);
+
/* Draw centre line.
(bottom half) */
pl_fline_r(ch->lp,
box_centre, top_whisker,
box_centre, box_top);
-
- /* Draw an outlier */
- pl_fcircle_r(ch->lp,
- box_centre,
- ch->data_bottom + (s->outlier - y_min ) * ordinate_scale,
- 5);
-
- pl_moverel_r(ch->lp, 10,0);
- pl_alabel_r(ch->lp,'l','c',"123");
+ /* Draw outliers */
+ for ( i = 0 ; i < n_data ; ++i )
+ {
+ if ( wvp[i]->v.f >= hinge[2] + step )
+ draw_outlier(ch, box_centre, wvp, i,
+ ( wvp[i]->v.f > hinge[2] + 2 * step )
+ );
+
+ if ( wvp[i]->v.f <= hinge[0] - step )
+ draw_outlier(ch, box_centre, wvp, i,
+ ( wvp[i]->v.f < hinge[0] - 2 * step )
+ );
+ }
/* Draw tick mark on x axis */
}
+
+
+void
+boxplot_draw_yscale(struct chart *ch , double y_max, double y_min)
+{
+ double y_tick;
+ double d;
+
+ if ( !ch )
+ return ;
+
+ ch->y_max = y_max;
+ ch->y_min = y_min;
+
+ y_tick = chart_rounded_tick(fabs(ch->y_max - ch->y_min) / 5.0);
+
+ ch->y_min = (ceil( ch->y_min / y_tick ) - 1.0 ) * y_tick;
+
+ ch->y_max = ( floor( ch->y_max / y_tick ) + 1.0 ) * y_tick;
+
+ ch->ordinate_scale = fabs(ch->data_top - ch->data_bottom)
+ / fabs(ch->y_max - ch->y_min) ;
+
+
+ /* Move to data bottom-left */
+ pl_move_r(ch->lp,
+ ch->data_left, ch->data_bottom);
+
+ for ( d = ch->y_min; d <= ch->y_max ; d += y_tick )
+ {
+ draw_tick (ch, TICK_ORDINATE, (d - ch->y_min ) * ch->ordinate_scale, "%g", d);
+ }
+
+}