yoppa.org


芸大 – 情報編集(Web) 2014

最終課題に向けて: 芸大電力消費量のデータをProcessing.jsでビジュアライズする 01

今日の内容

今回は、最終課題にむけて実際のデータを参照しながらどのように進めていくのか実践的に考えていきます。

最終課題で使用するデータとその形式

東京藝術大学の様々な場所でモニターした電力使用料のデータが入手できました。最終課題では、このデータを使用してProcessing.jsでビジュアライズしていきたいと思います。

データは、CSV形式と呼ばれるコンマ(,)区切りのテキストデータで記述されています。それぞれの場所ごとに1行ずつコンマ区切りで記述され、毎時間ごとの電力使用料が並んでいます。

データは1日ごとに別のファイルとして記録されていて、記録された日数分のファイルがデータとして存在しています。

各ファイルの最初の行は、「START」という文字列と、日付が入っています。また最後の行には「END」という文字列が記載されています。

  • ファイル名: yyyymmdd_ENE.CSV
  • 内容
START, yyyy/mm/dd
機器番号, 1時データ, 2時データ, 3時データ ... , 24時データ
機器番号, 1時データ, 2時データ, 3時データ ... , 24時データ
機器番号, 1時データ, 2時データ, 3時データ ... , 24時データ
...
機器番号, 1時データ, 2時データ, 3時データ ... , 24時データ
END

CSVのデータサンプル(2014年3月18日のデータ)

Processing.jsでデータを読み込む

1. 1日分のデータを読み込む(関数バージョン)

では、まず初めにこの形式のデータを読み込む簡単なプログラムを自作してみましょう。

// ファイルを指定して電力データを読み込む
// 関数(Function)バージョン

String date;   // 日付
int data[][];  // データの配列 data[行][列]

void setup() {
  // 画面初期化
  size(800, 1004);
  frameRate(30);
  // 文字サイズ
  textSize(8);
  // データ読み込み関数を実行
  loadData(”20140318_ENE.CSV”);
}

  // 結果を文字で表示
void draw() {
  // 背景
  background(0);
  // 日付
  text(date, 2, 10);
  // 全体を少し右下へ
  pushMatrix();
  translate(2, 20);
  // 行と列の数だけ繰り返し
  for (int i = 0; i < data.length - 1; i++) {
    for (int j = 0; j < 24; j++) {
      // データの数値を文字で画面に表示
      text(data[i][j], (width / 24.0) * j, 9 * i);
    }
  }
  popMatrix();
}

// ファイルを指定してデータを読み込む関数
void loadData(String filename) {
  // 行単位でデータを読み込み
  String[] rows = loadStrings(filename);
  // 最初の行はヘッダーとする
  String[] header = split(rows[0], ”,”);
  // ヘッダーの2番目の項目は日付
  date = header[1];
  // データ配列の準備
  data = new int[rows.length - 1][];
  // コンマ区切りで行のデータを分割
  for (int i = 1; i < rows.length - 1; i++) {
    int[] row = int(split(rows[i], ”,”));
    data[i - 1] = (int[]) subset(row, 1);
  }
}

このプログラムを実行すると、全てのデータがテキストで画面に描画されます。

実行結果

2. 1日分のデータを読み込む(クラスバージョン)

ファイルを指定してデータを読み込む機能は、繰り返し使用する一つの機能となります。関数として使用しても良いのですが、クラスとして実装したほうが後々拡張性やプログラムの可読性を考えると便利です。

「loadData()」関数と全く同じ機能をクラスにまとめてみます。クラス名は「EneTable」としました。クラスのソースは以下のようになります。

// EneTable
// 電力データ読み込み用クラス

class EneTable {
  String date;  // 日付
  int data[][]; // データ

  // コンストラクタ(初期化関数)
  // ファイルを指定して、データをパース
  EneTable(String filename) {
    String[] rows = loadStrings(filename);
    String[] header = split(rows[0], ",");
    date = header[1];    
    data = new int[rows.length - 1][];

    for (int i = 1; i < rows.length - 1; i++) {
      int[] row = int(split(rows[i], ","));
      data[i - 1] = (int[]) subset(row, 1);
    }
  }
}

このクラスを使用した表示のためのメインのプログラムは以下のようになります。

// ファイルを指定して電力データを読み込む
// クラス(Class)バージョン

EneTable ene;

void setup() {
  // 画面初期化
  size(800, 1004);
  frameRate(30);
  textSize(8);

  // データ読み込み用クラスEneTableをインスタンス化(初期化)
  ene = new EneTable("20140318_ENE.CSV");
}

// 結果を文字で表示
void draw() {
  // 背景
  background(0);
  // 日付
  text(ene.date, 2, 10);
  // 全体を少し右下へ
  translate(2, 20);
  // 行と列の数だけ繰り返し  
  for (int i = 0; i < ene.data.length-1; i++) {
    for (int j = 0; j < 24; j++) {
      // データの数値を文字で画面に表示
      text(ene.data[i][j], (width / 24.0) * j, 9 * i);
    }
  }
}

実行結果

3. 全ての日付のデータを読み込む

次にファイル読み込みの仕組みを工夫して、全ての日付けのデータを順番に表示するように変更してみます。

まず初めにファイル名一覧のテキストファイルを作成します。1ファイル名1行でテキスト形式で作成します。例えば以下のようになります。このファイル名に「filelist.txt」という名前をつけdataフォルダ内に保存しておきます。

20140318_ENE.CSV
20140319_ENE.CSV
20140320_ENE.CSV
...
20140701_ENE.CSV

ファイルリスト(filelist.txt)

このファイルリストを使用して、全ての日時のデータを順番に表示するプログラムを作成してみましょう! 「EneTable」クラスに変更はありません。

// ファイルを指定して電力データを読み込む
// クラス(Class)バージョン

EneTable ene;
String[] filelist;
int dataNum = 0;

void setup() {
  // 画面初期化
  size(800, 1004);
  frameRate(30);
  textSize(8);

  filelist = loadStrings(”filelist.txt”); 
  // データ読み込み用クラスEneTableをインスタンス化(初期化)
  ene = new EneTable(filelist[dataNum]);
}

// 結果を文字で表示
void draw() {
  // 背景
  background(0);
  // 日付
  text(ene.date, 2, 10);
  // 全体を少し右下へ
  translate(2, 20);
  // 行と列の数だけ繰り返し  
  for (int i = 0; i < ene.data.length-1; i++) {
    for (int j = 0; j < 24; j++) {
      // データの数値を文字で画面に表示
      text(ene.data[i][j], (width / 24.0) * j, 9 * i);
    }
  }

  // 一つ先のファイルを再読み込み
  // データ番号を更新
  dataNum++;
  // もしファイル数よりも多ければリセット
  if (dataNum > filelist.length - 1) {
    dataNum = 0;
  }
  // クラスを再度初期化する
  ene = new EneTable(filelist[dataNum]);
}

実行結果

4. 色を塗りわける

では、少し見せかたの工夫をしていきましょう。数値のテキストだけでなく背景色によってデータを表現してみましょう。

// ファイルを指定して電力データを読み込む
// クラス(Class)バージョン
// 

EneTable ene;
String[] filelist;
int dataNum = 0;

void setup() {
  // 画面初期化
  size(800, 1004);
  noStroke();
  frameRate(30);
  textSize(8);

  filelist = loadStrings(”filelist.txt”); 
  // データ読み込み用クラスEneTableをインスタンス化(初期化)
  ene = new EneTable(filelist[dataNum]);
}

// 結果を文字で表示
void draw() {
  // 背景
  background(0);
  // 日付
  fill(255);
  text(ene.date, 2, 10);
  pushMatrix();
  // 全体を少し右下へ
  translate(2, 20);
  // 行と列の数だけ繰り返し  
  for (int i = 0; i < ene.data.length-1; i++) {
    for (int j = 0; j < 24; j++) {
      // データの数値を文字で画面に表示
      float value = map(ene.data[i][j], 0, 100, 0, 255);
      if(value > 255){
        value = 255;
      }
      fill(value, 0, 255-value);
      rect((width / 24.0) * j, 9 * i - 6, (width / 24.0)-1, 8);
      fill(255,127);
      text(ene.data[i][j], (width / 24.0) * j, 9 * i);
    }
  }
  popMatrix();
  // 一つ先のファイルを再読み込み
  // データ番号を更新
  dataNum++;
  // もしファイル数よりも多ければリセット
  if (dataNum > filelist.length - 1) {
    dataNum = 0;
  }
  // クラスを再度初期化する
  ene = new EneTable(filelist[dataNum]);
}

実行結果

5. 色+長さで表現

では、最後に色だけでなく四角形の幅で値を表現してみましょう。さらに1日ごとに1フレームではなく、1時間で1フレームで表現してみましょう。

// ファイルを指定して電力データを読み込む
// クラス(Class)バージョン
// 

EneTable ene;
String[] filelist;
int dataNum = 0;
int hour = 0;

void setup() {
  // 画面初期化
  size(800, 1004);
  noStroke();
  frameRate(30);
  textSize(8);

  filelist = loadStrings(”filelist.txt”); 
  // データ読み込み用クラスEneTableをインスタンス化(初期化)
  ene = new EneTable(filelist[dataNum]);
}

// 結果を文字で表示
void draw() {
  // 背景
  background(0);
  // 日付
  fill(255);
  text(ene.date + ” : ”  + hour, 2, 10);
  pushMatrix();
  // 全体を少し右下へ
  translate(2, 20);
  // 行と列の数だけ繰り返し  
  for (int i = 0; i < ene.data.length-1; i++) {
    // データの数値を文字で画面に表示
    float graphWidth = map(ene.data[i][hour], 0, 1000, 0, width);
    if (graphWidth > width) {
      graphWidth = width;
    }
    float value = map(ene.data[i][hour], 0, 100, 0, 255);
    if (value > 255) {
      value = 255;
    }
    fill(value, 0, 255-value);
    rect(0, 9 * i - 6, graphWidth, 8);
  }

  popMatrix();
  // 一つ先のファイルを再読み込み
  // データ番号を更新
  hour++;
  if (hour > 23) {
    hour = 0;
    dataNum++;
  }
  // もしファイル数よりも多ければリセット
  if (dataNum > filelist.length - 1) {
    dataNum = 0;
  }
  // クラスを再度初期化する
  ene = new EneTable(filelist[dataNum]);
}

実行結果

次回はさらに凝ったビジュアライズに挑戦します!

サンプルファイルのダウンロード

今回紹介した全てのサンプルコードは以下からダウンロードが可能です。