file: stats.c
/* compute and display statistics on data covered by current brush */ #include <math.h> #include <stdlib.h> #include "XmdvTool.h" #include "util.h" #define BP_WIDTH 200 /* box plot width */ #define MED_DIAM 8 /* diameter for filled circle (median) */ #define MEAN_DIAM 6 /* diameter for open circle (mean) */ #define OUT_DIAM 4 /* diameter for filled circles (outliers) */ extern Widget appShell; extern char names[MAXDIM][MAXLABEL]; extern double *br_pos, *br_size; extern double dim_min[MAXDIM], dim_max[MAXDIM]; extern unsigned long color_cells[NUM_COLOR_CELLS]; extern GC gc; /* is this a color display? */ extern int COLOR; extern int X_SIZE, Y_SIZE; Arg wargs[10]; int n; /* stat variables */ int stat_open = FALSE; /* statistics in double and integer (normalized) forms * median, low and upper quartile, lower and upper adjacent values (low/upper * -/+ 1.5 (upper-lower)), mean, standard deviation */ struct stats_tag { double median, ql, qu, lav, uav, mean, stdev; int imedian, iql, iqu, ilav, iuav, imean, istdev; } stats[MAXDIM]; extern int dims, data_size; double bmin, bmax; /* brush extents for current dimension */ /* compute statistics, determine integer positions within box plot, return * number of covered points */ int GetStats(double *t_data, int i) { double buf[MAXDIM], total, range; int list_size, j; int comp_double(double *, double *); /* initialize summation, size, and brush bounds */ if(data_size <= 0 || dims <= 0) return(0); total = 0.0; list_size = 0; bmin = br_pos[i] - br_size[i]/2.0; bmax = br_pos[i] + br_size[i]/2.0; /* fill up list with covered points, updating summation */ for(j = 0;j < data_size;j++) { get_data(buf, j); if(covered(buf)) { t_data[list_size] = buf[i]; total += buf[i]; list_size++; } } /* handle empty brush */ if(list_size == 0) return(0); /* sort list */ qsort(t_data, list_size, sizeof(double), comp_double); /* compute median, quartiles, adjacent */ stats[i].median = t_data[list_size/2]; stats[i].ql = t_data[list_size/4]; stats[i].qu = t_data[3*list_size/4]; range = 1.5 * (stats[i].qu -stats[i].ql); if((stats[i].ql - range) < bmin) stats[i].lav = bmin; else stats[i].lav = stats[i].ql - range; if((stats[i].qu + range) > bmax) stats[i].uav = bmax; else stats[i].uav = stats[i].qu + range; /* compute mean, standard deviation */ stats[i].mean = total/(double)list_size; total = 0.0; for(j = 0;j < list_size;j++) total += (t_data[j] - stats[i].mean) * (t_data[j] - stats[i].mean); stats[i].stdev = sqrt(total/(double)list_size); stats[i].imedian = (double) BP_WIDTH * (stats[i].median - bmin)/ (bmax - bmin); /* compute pixel positions within box plot for each statistic */ stats[i].iql = (double) BP_WIDTH * (stats[i].ql - bmin)/ (bmax - bmin); stats[i].iqu = (double) BP_WIDTH * (stats[i].qu - bmin)/ (bmax - bmin); stats[i].ilav = (double) BP_WIDTH * (stats[i].lav - bmin)/ (bmax - bmin); stats[i].iuav = (double) BP_WIDTH * (stats[i].uav - bmin)/ (bmax - bmin); stats[i].imean = (double) BP_WIDTH * (stats[i].mean - bmin)/ (bmax - bmin); stats[i].istdev = (double) BP_WIDTH * (stats[i].stdev)/ (bmax - bmin); return(list_size); } /* spacing to use within drawing */ int gap_small = 3; int gap_big = 5; int std_shift = 5; int extra = BP_WIDTH/2; /* draw box plot */ void StatsDoRedraw(void) { double *t_data; int i, j, outx; Widget stat_canvas_w, stat_count; int box_height, box_width, text_height, dummy, stdpos, count; XFontStruct *font; unsigned long font_height; XCharStruct overall; int label_len, tmp_len, max_left, max_right; char num_label[40]; /* if the widget isn't open, just return */ if(!stat_open) return; /* get statistics canvas widget */ stat_canvas_w = WcFullNameToWidget(appShell, "*stats_canvas"); if(!stat_canvas_w) { fprintf(stderr, "Error finding *stats_canvas\n"); return; } /* get point count label widget */ stat_count = WcFullNameToWidget(appShell, "*stats_count"); if(!stat_count) { fprintf(stderr, "Error finding *stats_count\n"); return; } XClearWindow(XtDisplay(stat_canvas_w), XtWindow(stat_canvas_w)); /* allocate space for list of data */ if(data_size > 0) { t_data = (double *)malloc(data_size * sizeof(double)); if(t_data == NULL) { printf("couldn't allocate space for list in GetStats\n"); exit(-1); } } /* if the brush isn't covering anything, just fill label and return */ count = GetStats(t_data, 0); n = 0; sprintf(num_label, "Number of points: %d", count); XtSetArg(wargs[n],XtNlabel,num_label); n++; XtSetValues(stat_count,wargs,n); if(count == 0) return; /* compute the number of pixels needed for the left column info */ font = XQueryFont(XtDisplay(stat_canvas_w), XGContextFromGC(gc)); max_left = 0; for(i = 0;i < dims;i++) { tmp_len = XTextWidth(font, names[i], strlen(names[i])); if(max_left < tmp_len) max_left = tmp_len; sprintf(num_label, "min = %8f", br_pos[i] - br_size[i]/2.0); tmp_len = XTextWidth(font, num_label, strlen(num_label)); if(max_left < tmp_len) max_left = tmp_len; } /* compute the number of pixels needed for the right column info */ max_right= 0; for(i = 0;i < dims;i++) { sprintf(num_label, "max = %8f", br_pos[i] + br_size[i]/2.0); tmp_len = XTextWidth(font, num_label, strlen(num_label)); if(max_right < tmp_len) max_right = tmp_len; } /* add some extra space for the overflow of the box plot */ max_right += extra; max_left += extra; /* get the height of the text */ XTextExtents(font, names[0], strlen(names[0]), &dummy, &dummy, &dummy, &overall); text_height = overall.ascent+overall.descent; /* compute the dimensions for a box */ box_height = 2 * text_height + 3 * gap_small; box_width = max_left + max_right + BP_WIDTH + 4 * gap_small; /* set the new x/y dimensions of the stats tool */ n = 0; XtSetArg(wargs[n],XtNwidth,box_width); n++; XtSetArg(wargs[n],XtNheight,dims * box_height); n++; XtSetValues(stat_canvas_w,wargs,n); /* for each dimension, draw box plot */ for(i = 0;i < dims;i++) { /* do the dimension name, minimum, and maximum */ XSetForeground(XtDisplay(stat_canvas_w), gc, color_cells[CCELL_TEXT]); XDrawString(XtDisplay(stat_canvas_w), XtWindow(stat_canvas_w), gc, gap_small, i * box_height + text_height, names[i], strlen(names[i])); sprintf(num_label, "min = %8f", br_pos[i] - br_size[i]/2.0); XDrawString(XtDisplay(stat_canvas_w), XtWindow(stat_canvas_w), gc, gap_small, i * box_height + 2 * text_height + gap_small, num_label, strlen(num_label)); sprintf(num_label, "max = %8f", br_pos[i] + br_size[i]/2.0); XDrawString(XtDisplay(stat_canvas_w), XtWindow(stat_canvas_w), gc, 3 * gap_small + max_left + BP_WIDTH + extra, i * box_height + 2 * text_height, num_label, strlen(num_label)); /* draw vertical lines to show brush extents */ XDrawLine(XtDisplay(stat_canvas_w), XtWindow(stat_canvas_w), gc, max_left + 2 * gap_small, i * box_height + gap_big, max_left + 2 * gap_small, (i+1) * box_height - gap_big); XDrawLine(XtDisplay(stat_canvas_w), XtWindow(stat_canvas_w), gc, max_left + 2 * gap_small+BP_WIDTH, i * box_height + gap_big, max_left + 2 * gap_small+BP_WIDTH, (i+1) * box_height - gap_big); /* do the median, quartile box, adjacent lines, and outliers in one color */ XSetForeground(XtDisplay(stat_canvas_w), gc, color_cells[CCELL_STAT1]); XDrawRectangle(XtDisplay(stat_canvas_w), XtWindow(stat_canvas_w), gc, 2 * gap_small + max_left + stats[i].iql, i * box_height + gap_big, stats[i].iqu - stats[i].iql, box_height - 2 * gap_big); XFillArc(XtDisplay(stat_canvas_w), XtWindow(stat_canvas_w), gc, 2 * gap_small + max_left + stats[i].imedian - MED_DIAM/2, i * box_height + box_height/2 - MED_DIAM/2, MED_DIAM, MED_DIAM, 0, 64*360); /* adjacent lines are dashed, sort of side-ways T's (should be square brackets, * to be "official" Tukey box plots) */ XSetLineAttributes(XtDisplay(stat_canvas_w), gc, 0, LineOnOffDash, CapButt, JoinMiter); XDrawLine(XtDisplay(stat_canvas_w), XtWindow(stat_canvas_w), gc, 2 * gap_small + max_left + stats[i].ilav, i * box_height + gap_big, 2 * gap_small + max_left + stats[i].ilav, (i+1) * box_height - gap_big); XDrawLine(XtDisplay(stat_canvas_w), XtWindow(stat_canvas_w), gc, 2 * gap_small + max_left + stats[i].iuav, i * box_height + gap_big, 2 * gap_small + max_left + stats[i].iuav, (i+1) * box_height - gap_big); XDrawLine(XtDisplay(stat_canvas_w), XtWindow(stat_canvas_w), gc, 2 * gap_small + max_left + stats[i].iql, i * box_height + box_height/2, 2 * gap_small + max_left + stats[i].ilav, i * box_height + box_height/2); XDrawLine(XtDisplay(stat_canvas_w), XtWindow(stat_canvas_w), gc, 2 * gap_small + max_left + stats[i].iqu, i * box_height + box_height/2, 2 * gap_small + max_left + stats[i].iuav, i * box_height + box_height/2); XSetLineAttributes(XtDisplay(stat_canvas_w), gc, 0, LineSolid, CapButt, JoinMiter); /* figure out which points are outliers (outside adjacent points) and plot */ count = GetStats(t_data, i); for(j = 0;j < count;j++) if(t_data[j] > stats[i].uav || t_data[j] < stats[i].lav) { outx = (double)BP_WIDTH * (t_data[j] - bmin) / (bmax - bmin); XFillArc(XtDisplay(stat_canvas_w), XtWindow(stat_canvas_w), gc, 2 * gap_small + max_left + outx - OUT_DIAM/2, i * box_height + box_height/2 - OUT_DIAM/2, OUT_DIAM, OUT_DIAM, 0, 64*360); } /* change color and draw mean and standard deviation */ XSetForeground(XtDisplay(stat_canvas_w), gc, color_cells[CCELL_STAT2]); XDrawArc(XtDisplay(stat_canvas_w), XtWindow(stat_canvas_w), gc, 2 * gap_small + max_left + stats[i].imean - MEAN_DIAM/2, i * box_height + box_height/2 - MEAN_DIAM/2, MEAN_DIAM, MEAN_DIAM, 0, 64*360); /* standard deviation is indicated by angle brackets */ stdpos = stats[i].imean - stats[i].istdev; XDrawLine(XtDisplay(stat_canvas_w), XtWindow(stat_canvas_w), gc, 2 * gap_small + max_left + stdpos, i * box_height + box_height/2, 2 * gap_small + max_left + stdpos + std_shift, (i+1) * box_height - gap_big); XDrawLine(XtDisplay(stat_canvas_w), XtWindow(stat_canvas_w), gc, 2 * gap_small + max_left + stdpos, i * box_height + box_height/2, 2 * gap_small + max_left + stdpos + std_shift, i * box_height + gap_big); stdpos = stats[i].imean + stats[i].istdev; XDrawLine(XtDisplay(stat_canvas_w), XtWindow(stat_canvas_w), gc, 2 * gap_small + max_left + stdpos, i * box_height + box_height/2, 2 * gap_small + max_left + stdpos - std_shift, (i+1) * box_height - gap_big); XDrawLine(XtDisplay(stat_canvas_w), XtWindow(stat_canvas_w), gc, 2 * gap_small + max_left + stdpos, i * box_height + box_height/2, 2 * gap_small + max_left + stdpos - std_shift, i * box_height + gap_big); } } /* compare 2 doubles - used by qsort */ int comp_double(double *a, double *b) { if(*a < *b) return (-1); if(*a > *b) return (1); return(0); } /* callback to redraw statistics canvas */ void StatsRedrawCanvas(Widget w, caddr_t client_data, XawDrawingAreaCallbackStruct *call_data) { StatsDoRedraw(); } /* initialize the stats window */ void StatsInit(Widget w, caddr_t client_data, caddr_t call_data) { /* set the global tool open flag */ stat_open = TRUE; } /* * StatsPopDown() - Callback when stats tool is popped down. */ void StatsPopDown(Widget w, caddr_t client_data, caddr_t call_data) { /* reset stats open flag */ stat_open = FALSE; } /* * StatsUpdate() - Update the stats window */ void StatsUpdate(void) { /* if brush tool is not open, do nothing */ if(!stat_open) return; StatsDoRedraw(); }
Back to Source File Index
C++ to HTML Conversion by ctoohtml