Processing(データの取り込み1)

Processingでデータの取り込みについてまとめておきます。

標準でいくつかの形式を取り込めますが、今回はCSVデータの取り込み方法をまとめています。

void setup()
{
    size(1920, 1080);
    background(255, 255, 255);
}
void draw()
{
}

変数を準備する

最初に、取り込んだデータを入れておく変数を準備します。変数はtable型を使います。

Table dataArray;

void setup()
{
    size(1920, 1080);
    background(255, 255, 255);
}
void draw()
{
}

データを取り込む

準備した変数にデータを取り込みます。注意点として、取り込み対象のCSVは実行しているProcessingのファイル(*.pde)と同じディレクトリに置かれている必要があります。
(もしくはパスで指定します)

第1引数でファイル名、第2引数ではヘッダの有無、ファイルタイプを指定できます。 ファイルに拡張子がついている場合は、ファイルタイプを指定する必要はありません。

指定 内容
header データの1行目をヘッダとして2行目以降を取り込む
csv カンマ区切りのデータ
tsv tab区切りのデータ
bin バイナリファイル

ヘッダとファイルタイプを同時に指定したい場合は、カンマで区切ります。

Processingはファイルの読み書きでUTF-8エンコーディングが使用されます。

dataArray = loadTable("number.csv", "header,csv");

Table dataArray;
void setup()
{
    dataArray = loadTable("number.csv");    
    size(1920, 1080);
    background(255, 255, 255);
}
void draw()
{
}

取り込んだデータは配列のような入れ物に入ります。

CSVExcelで開くと下記のように、行は1から順番に、列はAから順番に表示されます。

A B C D E
1 a b c d e
2 f g h i j
3 k l m n o
4 p q r s t
5 u v w x y

このデータを取り込むと、行列ともに0から始まる番号になります。

0 1 2 3 4
0 a b c d e
1 f g h i j
2 k l m n o
3 p q r s t
4 u v w x y

データをTableから取り出す

Table変数からデータを取り出す場合、列と行を指定します。 また、どの型で取り出すかによって使うメソッドを変える必要があります。

データ型 メソッド
int(整数) getInt()
float(浮動小数 getFloat()
string(文字列) getString()

下記表のようなテーブルがあった場合、"a"を取り出したければ行を0、列を0と指定します。”o”を取り出したい場合は行を2、列を4と指定します。

string str = dataArray.getString(0, 0);

0 1 2 3 4
0 a b c d e
1 f g h i j
2 k l m n o
3 p q r s t
4 u v w x y
Table dataArray;
void setup()
{
    dataArray = loadTable("number.csv"); 
    size(1920, 1080);
    background(255, 255, 255);
}
void draw()
{
    string str = dataArray.getString(0, 0);
}

すべてのデータを取り出す

取り込んだデータを順番に取り出したい場合、1つずつデータを指定していくと何度も取り出しのメソッドを書く必要があります。

そこで、繰り返し分を使って取り出していきます。

例として下記テーブルを取り込んでx,y座標にsizeの大きさで丸を描いてみます。

0 1 2
0 x y size
1 1700 100 150
2 900 300 100
3 100 500 300
4 1300 700 200
5 500 900 250

このデータにはヘッダがあるので、取り込み時に"header"を指定します。 for文の条件は、今回であればfor (int i = 0; i < 5; i++)となりますが、5という数字はあらかじめcsvの行数調べておく必要があり、csvデータに合わせて書き換える必要があります。
そこで、dataArray.getRowCount()を使ってtable変数の中に格納されているデータの行数を調べます。

今回は使用していませんが、getColumnCount()で列数を取り出すこともできます。

Table dataArray;
void setup()
{
    dataArray = loadTable("number.csv", "header"); 
    size(1920, 1080);
    background(255, 255, 255);
}
void draw()
{
    for (int i = 0; i < dataArray.getRowCount(); i++)
    {
        int coordX = dataArray.getInt(i, 0);
        int coordY = dataArray.getInt(i, 1);
        int size = dataArray.getInt(i, 2);
        
        ellipse(coordX, coordY, size, size);
    }
}

f:id:ArtificialArts:20180524073637j:plain

データ取り込み時にheaderを指定した場合、ヘッダに入力されている文字列がその列の名前となります。 この名前を使用してデータを取り出すこともできます。

Table dataArray;
void setup()
{
    dataArray = loadTable("number.csv", "header"); 
    size(1920, 1080);
    background(255, 255, 255);
}
void draw()
{
    for (int i = 0; i < dataArray.getRowCount(); i++)
    {
        int coordX = dataArray.getInt(i, "x");
        int coordY = dataArray.getInt(i, "y");
        int size = dataArray.getInt(i, "size");
        
        ellipse(coordX, coordY, size, size);
    }
}

この場合の利点として、名前が変わっていなければ行が入れ替わってもそのままプログラムを動かすことができます。

データの加工

使おうとしているデータが、そのままの数値では使えないという場合が多々あるかと思います。

例として、星の座標データを取り込み、その位置に点を書いてみます。

データの中は下記表のようになっていて、PM_RA:赤経(mas/yr)をX軸、PM_DEC:赤緯(mas/yr)をY軸、VMAG(視等級)を大きさに当ててみます。

0 1 2
pm_ra pm_dec vmag
0 -546.01 -1223.08 -1.44
1 19.99 23.67 -0.62
2 -1093.45 -1999.4 -0.05
3 -3678.19 481.84 -0.01
4 201.02 287.46 0.03
Table dataArray;
void setup()
{
    dataArray = loadTable("stars.csv", "header"); 
    size(1920, 1080);
    background(255, 255, 255);
}
void draw()
{
    for (int i = 0; i < dataArray.getRowCount(); i++)
    {
        int coordX = dataArray.getInt(i,0);
        int coordY = dataArray.getInt(i,1);
        int size = dataArray.getInt(i,2);
        
        ellipse(coordX, coordY, size, size);
    }
}

f:id:ArtificialArts:20180524080036j:plain

これだとうまく表示されません。原因は、取り込んだ数値の範囲が合っていないためです。 描画範囲はXを1920、Yを1080で設定していますが、Xに使用しているPM_RAは-3678.19から2220.12までの数値、Yに使用しているPM_DECは-1999.4から952.11までの数値、サイズに使用しているVMAGは-1.44から3.99の数値となっています。
そのため、描画範囲から外れている数値は画面外にあるので見えず、サイズも0やマイナスのものは描画されていません。

これはmap関数もしくはnorm関数で解決することができます。

map関数を使うと、ある最大最小値の中のaという値を、別の最大値最小値の中のbという値に変換することができます。

f:id:ArtificialArts:20180526224024j:plain

割合で考えるとわかりやすく、最小値0、最大値100の時の40の値は100という範囲の40%の値で、最小値から数えて40となります。
これを最小値50、最大値250の範囲に変換すると、200の範囲になりますので40%で80となり、最小値から数えた40%の値で130を得ることができます。

VMAGは、このデータでは3.99が最も小さく-1.44が最も大きいので、最小値が3.99、最大値が-1.44で関数に入れています。

Table dataArray;
void setup()
{
    dataArray = loadTable("stars.csv", "header"); 
 
    
    size(1920, 1080);
    background(255, 255, 255);
}
void draw()
{
    for (int i = 0; i < dataArray.getRowCount(); i++)
    {
        float coordX = map(dataArray.getInt(i,0), -3678.19, 2220.12, 0, width);
        float coordY = map(dataArray.getInt(i,1), -1999.4, 952.11, 0, height);
        float size = map(dataArray.getInt(i,2), 3.99, -1.4, 1, 6);
        
        ellipse(coordX, coordY, size, size);
    }
}

f:id:ArtificialArts:20180525075857j:plain

norm関数はmap関数とほとんど同じですが、出力値の範囲が0~1の間で固定です。関数に渡す値は少なくなりますが、map関数と同じように使用するには、そのままの値では使用できません。最小値を書く必要がありませんが、最小値が0以外の場合は工夫する必要があります。

Table dataArray;
void setup()
{
    dataArray = loadTable("stars.csv", "header"); 
 
    
    size(1920, 1080);
    background(255, 255, 255);
}
void draw()
{
    for (int i = 0; i < dataArray.getRowCount(); i++)
    {
        float coordX = norm(dataArray.getInt(i,0), -3678.19, 2220.12) * width;
        float coordY = norm(dataArray.getInt(i,1), -1999.4, 952.11) * height;
        float size = norm(dataArray.getInt(i,2), 3.99, -1.4) * 5 + 1;
        
        ellipse(coordX, coordY, size, size);
    }
}

長くなってしまったので次に続きます。