Pequeños Múltiples

Había una vez una empresa llamada CitiSent, donde una de las primeras ideas que tuvimos para ofrecer servicios de visualización se refería a comprender y comparar la calidad de vida de las distintas ciudades del país1.

Para construir esta visualización desarrollamos un tesauro2 de 6 grandes temas urbanos que generaban un indicador de percepción a partir del análisis de texto (sentiment analysis) en redes sociales. Cada uno de estos indicadores determina el tamaño de un pétalo, debidamente codificado con un color. La idea es que este sistema de representar (y condensar) la imformación permite comparar muy eficientemente las diferencias y contrastes entre los datos (las disparidades). Esta modalidad se denomina pequenos múltiples (small multiples). Esto es similar a las caras de Chernoff.

show code

CitiPulse model;
Petal current;

color[] c = {
#D63F09,
#FFA94D,
#FAE203,
#8FCB35,
#00828E,
#00426E,
#A64DFF,
#EB12EB,
#AE1D04,
#FF8408,
#DECC02,
#7EB331,
#0F8CA5,
#135589,
#8A27EC,
#CA0DCA,
#870101,
#C46200,
#B8AE01,
#6C9A2E,
#2096C0,
#2D6EAD,
#6C00D7,
#A809A8,
#690808,
#984805,
#929100,
#59802A,
#31A1DA,
#4688D1,
#4C0098,
#860486
};
String file;

float MAX = 120; // radio máximo del pétalo
float offset = 5;
color currentCol = color(255);

void setup() {
int side = 350;
size(350, 350);
smooth();
noStroke();
model = new CitiPulse(width/2, height/2);
populateCitiPulse(model);
current = new Petal();
file = ""+year()+month()+day()+hour()+minute()+".mov";
}

/**
*
* alfa + PI
* beta = ––––------
* 2
*
*
*
* MAX * sin(alfa/2)
* radio = -----------------
* sin(alfa/2) + 1
*
*/

void draw() {
background(255);
noFill();
stroke(#E3E3E3);
strokeWeight(1);
ellipse(width/2, height/2, MAX*2 + offset*2, MAX*2 + offset*2);
model.draw();
}

void keyPressed() {
if (key == 'v') {
for (int i = 0; i < model.petal.length; i++) {
println("petal["+i+"].value = "+model.petal[i].value);
}
println("---------------------------------------------");
}
if (key == ' ') {
populateCitiPulse(model);
}
if (key == 'x') {
exit();
}
if (key == 's') {
saveFrame("img/citipulse-#####.png");
}
}

void mouseMoved() {
currentCol = get(mouseX, mouseY);
}

void mousePressed() {
println(current.name+", cuyo valor es "+current.value);
}

class CitiPulse {

float x, y;
Petal[] petal;

CitiPulse(float x, float y) {
this.x = x;
this.y = y;
}

void draw() {
float offset = 5;
for (int i = 0; i < petal.length; i++) {
pushMatrix();
{
translate(x, y);
rotate(petal[i].alfa * (float)i);
petal[i].be();
}
popMatrix();
}
}
}

void populateCitiPulse(CitiPulse mod) {

int num = round(random(3, 10)); // número de pétalos o términos de primer nivel

float alfa = TWO_PI / (float)num;
float beta = (alfa + PI) / 2;

float radio = (MAX * sin(alfa/2))/(1 + sin(alfa/2));
float ac = MAX - radio;

println(" num = "+num);
println(" alfa = "+alfa+"\t ("+degrees(alfa)+" grados)");
println(" beta = "+beta);
println("radio = "+radio);
float result = radio + ac;
println(" AC = "+ac+"\t radio + AC = "+result);
println("---------------------------------------------");

mod.petal = new Petal[num];

for (int i = 0; i < mod.petal.length; i++) {
mod.petal[i] = new Petal();
mod.petal[i].alfa = alfa;
mod.petal[i].beta = beta;
mod.petal[i].r = radio;
mod.petal[i].ac = ac;
mod.petal[i].x = mod.x;
mod.petal[i].y = mod.y;

// lo único de cada pétalo
mod.petal[i].value = random(.2, 1);
mod.petal[i].name = "Elemento "+i;
mod.petal[i].c = c[i];
}
}

class Petal {
float x, y;
color c, co; // color & color over
float value;
String name;
int seed;

/* los valores a continuación son comunes para todos los pétalos */
float alfa, beta;
float r; // radio
float ac; // distancia desde el centro de la flor al centro del círculo

Petal() {
seed = round(random(99999));
}

Petal(float x, float y, float value, color col, String name) {
this.x = x;
this.y = y;
this.value = value;
this.name = name;
c = col;
}

void be() {
noiseSeed(seed);
value = noise(seed + (float)millis()/6000);

if (over(currentCol)) {
current = this;
co = color(red(c)-25, green(c)-25, blue(c)-25);
draw(0, 0, r + offset, co);
}

draw(offset, 0, r, c);

}

boolean over(color col) {
if (c == col) {
return true;
}
else {
return false;
}
}

void draw(float ox, float oy, float radio, color col) {
fill(col);
noStroke();
pushMatrix();
{
translate(ox, oy);
beginShape();
vertex(0, 0);

for (float t = -beta; t <= beta; t+=.1) {

float xpos = value*cos(t)*radio;
float ypos = value*sin(t)*radio;

vertex(ac*value + abs(r-radio) + xpos, ypos);
}
endShape(CLOSE);
}
popMatrix();
}
}

Este ejemplo de la unidad de visualización puede ser regenerado al presionar ESPACIO, pero es preciso ganar foco sobre el elemento haciendo click sobre él

Notas
  1. a este servicio lo llamamos CitiPulse []
  2. Un tesaurio es un diccionario jerárquico de palabras. En el caso de Citipulse definimos 6 grandes categorías: Medioambiente, Movilidad, Entorno Urbano, Sociabilidad, Gobernanza y Economía Local y Oportunidades. Cada una de ella dividias, a su vez, en otras sub-categorías, hasta completar un vocabulario de alrededor de 500 palabras. []

Agregar un comentario

Su dirección de correo no se hará público. Los campos requeridos están marcados *