データセットは行と列として保持される場合が多くあるので、p5.jsでもその扱いを容易にするp5.Tableオブジェクトが提供されています。行と列で構成されるスプレッドシート(Excelなどで言う表)を扱ったことがあれば、コードから同じように操作できます。p5.jsでは、ファイルから表を読み取ったり直接作成することができます。また行や列はどれでも読み取りと書き込みが可能で、表の個々のセル(マス)を修正することもできます。以降では特に表データの操作方法を見ていきます。
表はセルのグリッドです。行は水平方向に並ぶ要素で、列は垂直方向に並ぶ要素です。データは個々のセルや、行、列から読み取ることができます。
表データは通常、行をカンマやタブ文字で区切ったプレーンテキストです。カンマ区切りの値(comma-separated values)のファイルはCSVファイルと呼ばれ、拡張子.csvが用いられます。タブを使用した場合の.tsvも時々使われます。
CSYやTSVファイルを読み込むには、まずそのファイルをスケッチのフォルダ内に配置し、loadTable()関数を使ってデータを取得します。
次のサンプルは、ボストン レッドソックスで強打者としてならしたデビッド・オルティーズの年度別成績を扱ったものです。下図は、A列から順に年度、ホームラン数、打点数、平均打率の数字を並べたExcelファイルの画面です。
ExcelファイルをCSV形式に変換して保存するには次のようにします。
- [ファイル]→[名前を付けて保存]を選択
- [名前を付けて保存]画面の[ファイルの種類]ドロップダウンメニューで[CSV UTF-8(カンマ区切り)]を選択
- [保存]ボタンをクリック
保存したCSVファイルをテキストエディタで開くと、下図のように表示されます。データのひとかたまりはカンマで区切られ、データ同士は改行で区切られた形です。
目次
表の読み取り
このデータをloadTable()関数でp5.jsに読み込むと、表のデータはp5.Tableオブジェクトとして返されます。loadTable()関数は、setup()で安全にアクセスできるように、preload()内で呼び出します。
setup()内では、forループを使って各行を走査します。forループの条件にはp5.TableオブジェクトのgetRowCount()メソッドが利用できます(データ内の行数を数えます)。データは1999年から2014年までのものなので、列数は16個です。
let stats;
function preload() {
// CSVファイルを読み取り、p5.Tableオブジェクトを得る
stats = loadTable('ortiz.csv');
}
function setup() {
// データの行数を得る
print(stats.getRowCount()); // 16個
// データの行数分だけ繰り返す
for (let i = 0; i < stats.getRowCount(); i++) {
// i行の0、1、2、3列から値を得て変数に代入
const year = stats.get(i, 0);
const homeRuns = stats.get(i, 1);
const rbi = stats.get(i, 2);
const average = stats.get(i, 3);
print(year, homeRuns, rbi, average);
}
}
forループ内では、p5.Tableオブジェクトのget()メソッドを使ってデータを取得しています。get()メソッドはパラメータとして、読み取るデータの行と列を取ります。
loadTable()
説明
ファイルまたはURLの内容を読み取り、その値を持つp5.Tableオブジェクトを作成する。ファイルを指定する場合は、スケッチの “data”フォルダに置く必要がある。filenameパラメータにはまた、オンライン上のファイルへのURLも指定できる。デフォルトでは、ファイルはコンマ区切り(CSV形式)と見なされる。’header’オプションが含まれていると、ヘッダ行を探す(最初の行に項目名が含まれていて、これをデータに含みたくない場合に指定する)。
オプション:
csv – 表を、コンマ区切りの値として解析する
tsv – 表を、タブ区切りの値として解析する
header – この表はヘッダ(タイトル)行を持っている
オプションを複数渡すときには、カンマで区切って渡す。
loadTable(‘my_csv_file.csv’, ‘csv’, ‘header’);
ロードされ保存されるファイルにはすべて、UTF-8エンコーディングが用いられる。この関数は非同期で動作する。つまり、スケッチの実行中、次行が実行される前、完了していない可能性がある。preload()内でloadTable()を呼び出すことで、setup()とdraw()が呼び出される前、その操作が完了していることが保証される。preload()の外では、オブジェクトの処理にコールバック関数が使用できる。この関数はサイズが64MBまでのファイルの取得に適している。
シンタックス
loadTable(filename, options, [callback], [errorCallback])
loadTable(filename, [callback], [errorCallback])
p5.Table
説明
p5.Tableオブジェクトは複数の行と列を持つデータを保持する。従来のスプレッドシートとよく似ている。ゼロからダイナミックに生成することも、既存のファイルのデータを使って生成することもできる。
シンタックス
new p5.Table([rows])
パラメータ
rows p5.TableRow[]: p5.TableRowオブジェクトの配列
プロパティ
columns、rows
メソッド
getRowCount():表の行数の合計を返す
get():表の指定された行と列の値を取得する。行はそのIDで指定する。列はIDでもタイトルでも指定できる。
表の描画
次のサンプルでは、homeRunsという名前の配列を作成し、ファイルを読み取った後、setup()内でデータの保持に使用しています。このときは、get()メソッドではなく、グラフの数値として使用できるようにgetNum()メソッドを使用しています。
draw()内では、homeRuns.lengthプロパティの長さ分だけforループを繰り返す作業を2回行っています。1回めは配列の各データ項目分の垂直線を描画し、2回めは配列の各データ項目から折れ線グラフを描画しています。
beginShape()とendShape()、vertex()関数については、「2_7:p5.js シェイプを手作りする」で述べています。
let stats;
const homeRuns = [];
function preload() {
stats = loadTable('ortiz.csv');
}
function setup() {
createCanvas(480, 120);
const rowCount = stats.getRowCount();
// 表の行数分だけ繰り返す
for (let i = 0; i < rowCount; i++) {
// print(stats.getNum(i, 1));
// 各行のホームラン数を数値として得て、homeRuns配列に入れる
homeRuns[i] = stats.getNum(i, 1);
}
// print(homeRuns);
}
function draw() {
background(204);
stroke(153);
line(20, 100, 20, 20);
line(20, 100, 460, 100);
// 年度数分だけ、垂直線を描く
for (let i = 0; i < homeRuns.length; i++) {
const wx = map(i, 0, homeRuns.length - 1, 20, 460);
line(wx, 20, wx, 100);
}
noFill();
stroke(0);
beginShape();
// 折れ線グラフを描く
for (let j = 0; j < homeRuns.length; j++) {
const x = map(j, 0, homeRuns.length - 1, 20, 460);
const y = map(homeRuns[j], 0, 60, 100, 20);
vertex(x, y);
}
endShape();
}
このプログラムを実行すると、下図左の結果が描画されます。下図右は、Excelの機能を使って作成した折れ線グラフです。
p5.Table.getNum()
説明
表の指定された行と列から浮動小数を取得する。
p5.TableのgetObject()メソッドを使用すると、表データがオブジェクトとして取得できるので、ホームラン数以外のデータも楽に得られます。
let stats;
let ortizObject;
let rowNum = 0;
function preload() {
stats = loadTable('ortiz2.csv', 'csv', 'header');
}
function setup() {
createCanvas(480, 120);
// 表データをオブジェクトとして取得
ortizObject = stats.getObject();
print(ortizObject);
// データの行数
rowNum = Object.keys(ortizObject).length;
noLoop();
}
function draw() {
background(204);
stroke(153);
textSize(10)
line(20, 100, 20, 20);
line(20, 100, 460, 100);
// 年度数分だけ縦線を描き、年度を描画する
for (let i = 0; i < rowNum; i++) {
const wx = map(i, 0, rowNum - 1, 20, 460);
const year = ortizObject[i].year;
line(wx, 20, wx, 100);
text(year, wx - 10, 110);
}
noFill();
stroke(0);
beginShape();
// 折れ線グラフを描き、ホームラン数を描画する
for (let j = 0; j < rowNum; j++) {
const x = map(j, 0, rowNum - 1, 20, 460);
const homerun = ortizObject[j].homerun;
const y = map(homerun, 0, 60, 100, 20);
vertex(x, y);
text(homerun, x - 5, y)
}
endShape();
}
getObject()
説明
すべての表データを取得し、オブジェクトとして返す。列名が渡された場合には、そのタイトルを属性名に持つ行オブジェクトが保持される。
シンタックス
getObject([headerColumn])
29,470都市のデータ
次のサンプルでは、アメリカの各都市の緯度経度情報などをまとめた下図のようなCSVファイルを扱います。具体的なデータは2行めから29471行めまであるので、29470都市のデータがあることになります。
このCSVファイルの1行めにあるのはヘッダです。ヘッダは各列の内容を明確にするラベルを定義するもので、今の場合で言うと、zip(郵便番号)、state(州名)、city(都市名)、lat(緯度)、lng(経度)がヘッダとして定義されています。
ヘッダがあると、表を置き換えたp5.Tableオブジェクトで扱いやすくなります。たとえばgetNum()やgetString()メソッドでヘッダ名を指定して値が取得できます。そのためには、loadTable()関数に’header’を与えます。
let cities;
function preload() {
cities = loadTable('cities.csv', 'header');
}
function setup() {
// 最初の行の、city列とlat列の値を得る
const city = cities.getString(0, 'city');
print(city);
const latitude = cities.getNum(0, 'lat');
print(latitude);
}
次のサンプルを実行してマウスをキャンバス上に移動させると、アメリカの都市の位置を円で描いた地図が描画され、マウスをさらに動かすと円で描いた地図も移動します。
let cities;
function preload() {
cities = loadTable('cities.csv', 'header');
}
function setup() {
createCanvas(480, 240);
fill(255, 150);
noStroke();
// 最初の行の、city列とlat列の値を得る
const city = cities.getString(0, 'city');
print(city);
const latitude = cities.getNum(0, 'lat');
print(latitude);
}
const setXY = (lat, lng) => {
const x = map(lng, -180, 180, 0, width);
const y = map(lat, 90, -90, 0, height);
ellipse(x, y, 0.25, 0.25);
}
function draw() {
background(0);
const xoffset = map(mouseX, 0, width, -width * 3, -width);
translate(xoffset, -600);
scale(10);
for (let i = 0; i < cities.getRowCount(); i++) {
const latitude = cities.getNum(i, 'lat');
const longitude = cities.getNum(i, 'lng');
setXY(latitude, longitude);
}
}
getString()
説明
表(p5.Table)の指定された行と列から文字列値を取得する。行はそのIDで指定する。列はIDでもタイトルでも指定できる。
シンタックス
getString(row, column)
パラメータ
row – 整数:行のID(数値)
column – 文字列|整数:列名(ヘッダ、文字列)かID(数値)
戻り値
文字列
getNum()
説明
表(p5.Table)の指定された行と列から浮動小数値を取得する。行はそのIDで指定する。列はIDでもタイトルでも指定できる。
シンタックス
getNum(row, column)
パラメータ
row – 整数:行のID(数値)
column – 文字列|整数:列名(ヘッダ、文字列)かID(数値)
戻り値
数値