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();
}
C++ to HTML Conversion by ctoohtml