我们昨天已经画出了第一个柱状图。今天来看看怎么画的更好。一个标准的统计图表都应该有x轴和y轴标注的。首先我们要做在柱状图的四周给坐标轴空出位置。底部和右边各留出20,这样柱状图占的空间就变成380×130。写程序的时候一个好的习惯是把这些数字存放在变量里面,这样每次改动数据,就不用逐一去修改相关的数值了。所以我们用screen_width和screen_height来表示sketch的大小,right_margin和bottom_margin来表示留白。我们用函数line来画坐标轴。为了和背景区别开来,我们用灰色(100,100,100)作为坐标轴的颜色。

void setup()
{
    int screen_width = 400;
    int screen_height = 150;
    int left_margin = 20;
    int bottom_margin = 20;
 
    size(screen_width, screen_height);
    background(0);
 
    int[] numbers = { 2, 5, 3, 1, 6, 5, 9, 4, 7, 3, 2, 5, 1, 4, 2, 5 };
 
    int w = (screen_width-left_margin)/numbers.length;
    int max_number = max(numbers);
    int h = (screen_height-bottom_margin)/max_number;
 
    stroke(100,100,100);
    line(left_margin, (screen_height-bottom_margin), screen_width, (screen_height-bottom_margin));
    line(left_margin, (screen_height-bottom_margin), left_margin, 0);

    fill(255,255,255);
    for(int i=0;i<numbers.length;i++){
        rect(left_margin+i*w, (screen_height-bottom_margin), w, -h*numbers[i]);
    }
}

坐标轴上的文字标识用text(s, x, y)来现实。 s是要显示的文字,x,y分别是文字的坐标。x轴的文字比较简单,可以用数字在数组中的序列号来显示,就是i。坐标可以参照长方形的坐标。y轴的比较复杂一下。如果我们要显示4个标识,包括0点。每个标识之间间隔的距离就是(screen_height-bottom_margin)/(4-1)。文字应该是对应的数值大小。因为最大值是max_number,每个分隔就是max_number/(4-1)。我们需要一个循环来显示这些标识。同时我们想要在每个标识画辅助线来帮助了解每个长方形对应的数的大小。现在这个程序是这样的:

void setup()
{
    int screen_width = 400;
    int screen_height = 150;
    int left_margin = 20;
    int bottom_margin = 20;
    int y_num_ticks = 4;

    size(screen_width, screen_height);
    background(0);

    int[] numbers = { 2, 5, 3, 1, 6, 5, 9, 4, 7, 3, 2, 5, 1, 4, 2, 5 };

    int w = (screen_width-left_margin)/numbers.length;
    int max_number = max(numbers);
    int h = (screen_height-bottom_margin)/max_number;

    stroke(100,100,100);
    line(left_margin, (screen_height-bottom_margin), screen_width, (screen_height-bottom_margin));
    line(left_margin, (screen_height-bottom_margin), left_margin, 0);

    int y_tick_h = (screen_height-bottom_margin)/(y_num_ticks-1);
    int y_tick_val = max_number / (y_num_ticks-1);


    for(int i=0;i<y_num_ticks;i++){
        line(left_margin, (screen_height-bottom_margin)-i*y_tick_h, screen_width, (screen_height-bottom_margin)-i*y_tick_h);
        text(i*y_tick_val, 0, (screen_height-bottom_margin)-i*y_tick_h);
    }

    fill(255,255,255);
    for(int i=0;i<numbers.length;i++){
        rect(left_margin+i*w, (screen_height-bottom_margin), w, -h*numbers[i]);
        text(i, left_margin+i*w, screen_height);
    }
}

我们发现最上面的那条辅助线因为和sketch的边框重合,看不见了。所以在右边和上边我们也要空出一定空间。我们定义了right_margin =10和top_margin=10。在上面的程序里我们反复的用(screen_height-bottom_margin)和left_margin来表示柱状图的原点位置。所以让程序更简洁和易读的做法是定义新的变量chart_x, chart_y来存放这俩个数值。

还有,文字标识位置并没有对的很好。首先,x轴的文字最好在每个长方形对应的范围内居中,也最好不要贴着sketch的下边框。这里我们要用到textAlign()函数来定义文字的对齐模式。对于x轴的文字我们用textAlign(CENTER, TOP)。这样显示的文字就会以我们调用text()函数时给的x坐标为中点对齐,以y坐标向上对齐。因此我们的x和y也要相应改变。因为我们要按长方形的中点对齐,所以x应该是chart_x+i*w+w/2。我们想要文字离坐标轴距离是tick_padding =5,所以y应该是 chart_y+tick_padding。y轴的文字也可以用类似的方法对齐。建议大家自己先试一试,然后再看我们下面给出的答案。

void setup()
{
    int screen_width = 400;
    int screen_height = 150;
    int left_margin = 20;
    int bottom_margin = 20;
    int right_margin = 10;
    int top_margin = 10;
    int y_num_ticks = 4;
    int tick_padding = 5;

    int chart_x = left_margin;
    int chart_y = screen_height-bottom_margin;
    int chart_width = screen_width - left_margin - right_margin;
    int chart_height = screen_height - top_margin - bottom_margin;

    size(screen_width, screen_height);
    background(0);

    int[] numbers = { 2, 5, 3, 1, 6, 5, 9, 4, 7, 3, 2, 5, 1, 4, 2, 5 };

    int w = chart_width/numbers.length;
    int max_number = max(numbers);
    int h = chart_height/max_number;

    stroke(100,100,100);
    line(chart_x, chart_y, chart_x+chart_width, chart_y);
    line(chart_x, chart_y, chart_x, chart_y-chart_height);

    int y_tick_h = chart_height/(y_num_ticks-1);
    int y_tick_val = max_number / (y_num_ticks-1);

    textAlign(RIGHT, CENTER);
    for(int i=0;i<y_num_ticks;i++){
        line(chart_x, chart_y-i*y_tick_h, chart_x+chart_width, chart_y-i*y_tick_h);
        text(i*y_tick_val, chart_x-tick_padding, chart_y-i*y_tick_h);
    }

    fill(255,255,255);
    textAlign(CENTER, TOP);
    for(int i=0;i<numbers.length;i++){
        rect(chart_x+i*w, chart_y, w, -h*numbers[i]);
        text(i, chart_x+i*w+w/2, chart_y+tick_padding);
    }
}

我们已经能画最基本的柱状图了。感觉怎么样?

© 2011, 视物 | 致知. All rights reserved.

Related Posts:

  1. 光是画柱状图都这么麻烦啊,这些代码看的我头好疼啊,好疼。。。
    我在想有的可视化是否是直接通过其他软件画出来的
    比如说PS.AI什么的
    就很直观的表示的那种
    数据性不是很强的那种好像都是那样表示的吧?
    那么数据性要求很强的那种就需要这种
    不知道我这样理解对不对?
    求解

    • 我觉得Processing的目的就是用程序的方法,自动的方法来做原来设计师手动在做的一些事。这样的好处最直接的是当你做了一个可视化之后,以后可以很容易的用在别的数据上。第二个好处,现在随着数据量不断变大,很多已经到了人工很难处理,必须用程序处理的地步。最重要的好处是,如果输入数据由用户互动产生的时候,然后需要实时产生可视化。比如,这个柱状图的输入完全可以是用户每天微博的数量。每个用户输入自己帐号,程序去读微博数量,然后用这段程序显示成柱状图。

      我对AI不了解,不知道是不是也可以做这样的事。

    • 我个人的感觉你所说的是针对于实时的数据可视化网站或者是程序的解答吧,当然你说的好处我能够理解呢,就是能够及时的对数据作出反应,但是针对如何进数据进行美观化,不知道Processing是否能够做到,至少在我们现在所看到的案例当中很大一部分是直接作出的数据结论,而针对这个数据结论所作出的可视化图,那么就不需要用到Processing来进行实时的数据跟踪,也就是说不需要进行互动,直接告诉观者结论就OK了;非常谢谢你的耐心解答