テキストや数値を複合的に含むデータを保持し、 それらから利用者の希望するもののみ提示するような機能を考える。 ここでは単純化したシラバスを例に考え方を示す。
たとえば以下のようなシラバスに基づく科目情報を考える。
表1: 履修科目データの例
科目名 | 基礎プログラミング |
---|---|
科目コード | 154 |
担当教員 | 鳥海三輔 |
開講時期 | 春学期 |
概要 | プログラミングを通じて問題解決能力を向上を図る。 本講ではRubyを用い,データ処理の構造と考え方の基礎を学ぶ。 |
科目名 | 応用プログラミング |
科目コード | 254 |
担当教員 | 飯森大和 |
開講時期 | 秋学期 |
概要 | 問題解決を通じてプログラミング能力向上を図る。 本講ではプログラミング言語Cを通じて,問題解決に最適なアルゴリズムを 適用する方策を学ぶ。 |
科目名 | 応用もっけ論 |
科目コード | 39 |
担当教員 | 酒田育造 |
開講時期 | 春学期 |
概要 | 山形県庄内地方でよく使われる「もっけ」には様々な意味があり, 他の地域にもこのような多様性を持つ言葉がある。 その成立ちをふまえて正しくその地域の「もっけ」を理解し, 深い交流のきっかけとする。 |
以下同様の項目が多数続く…
このように,形式が同じものが多数あるようなデータを設計し, 検索できるデータベースを作成する過程を示す。
キー1 | 値1c |
---|---|
キー2 | 値2c |
キー3 | 値3c |
これ全体が「値」
|
1科目分の情報は1つのハッシュで表せる。そのハッシュ全体がさらに「値」 となり,それを複数集めることで科目データベースができ上がる。 ハッシュで表現した1科目分の「値」をどのように集めるかについて考察する。
-->上記のシラバス例をCSV化する。Calc のような表計算ソフトを利用して入力する例を示す。
A | B | C | D | E | |
---|---|---|---|---|---|
1 | 科目名 | 科目コード | 担当教員 | 開講時期 | 概要 |
2 | 基礎プログラミング | 154 | 鳥海三輔 | 春学期 | プログラミングを通して問題… |
3 | 応用プログラミング | 254 | 飯森大和 | 秋学期 | 問題解決を通してプログラミ… |
4 | 応用もっけ論 | 39 | 酒田育造 | 春学期 | 山形県庄内地方でよく使われ… |
「概要」の列は長くて編集しにくいかもしれない。LibreOffice Calc であれば「概要」の列見出し右クリックし、セルの書式設定を選ぶ。 「配置」タブで「テキストを自動的に折り返す」をチェックしてから列幅を 画面に収まる程度にすると多少緩和される。
RubyのCSVライブラリを用いて CSV
ファイルをハッシュとしてアクセスできる形式に変換する。syllabus.csv
のように1行目が見出しとして構成された CSV
ファイルであれば以下の流れで読み取りを行なう。短い手順なので irb
で実験してみよう。
require 'csv' => true csv = CSV.read("syllabus.csv", headers: true) => #<CSV::Table mode:col_or_row row_count:4>
通常 CSV.read で得られた値は、あたかも配列のように行ごとに取り出しで きる。見出し行を除く先頭行が添字0となる。
csv[0]
=> #<CSV::Row "科目名":"基礎プログラミング" "科目コード":"154" "担当教員":"鳥海
三輔" "開講時期":"春学期" "概要":"プログラミングを通じて問題解決能力を向上を図る。
\n本講ではRubyを用い,データ処理の構造と考え方の基礎を学ぶ">
このように取り出した行は、 あたかもハッシュのように特定の列の値を取り出せる。
csv[0]["科目コード"]
=> "154"
また、headers オプションを true にしたときはこれに加え、 1行目にある見出し項目をハッシュのキーのように指定することで 列全体を配列として取り出せる。
csv["科目名"]
=> ["基礎プログラミング", "応用プログラミング", "応用もっけ論"]
この様子を示すと以下の図のようになる。
CSV全体をCSV.read
で読み、変数 c
に代入したときのイメージ
列名1 | 列名2 | 列名3 | …… |
値1-1 | 値1-2 | 値1-3 | …… |
値2-1 | 値2-2 | 値2-3 | …… |
: : | : : |
: : | …… |
値n-1 | 値n-2 | 値n-3 | …… |
たとえば、列で縦方向に取り出す場合、c["列名2"]
とすると2列目の値が配列となり、
[値1-2, 値2-2, 値3-2, ..., 値1-2]
が得られる。
以上のしくみをふまえて、ヘッダつきで書かれた CSV ファイルを読み取るプログラムを示す。
#!/usr/bin/env ruby
# coding: utf-8
require 'csv' # CSVライブラリ
csvfile = "syllabus.csv" # シラバス定義CSVファイル
csv = CSV.read(csvfile, headers: true)
# 動作確認用に1行ずつ表示してみる
csv.each {|row|
# ppメソッドはpメソッドよりさらに見やすく値表示する
pp row.to_h # to_h メソッドはハッシュに変換してくれる
}
syllabus.csvを保存してから
上記プログラムを実行してみよ。
csv-read.rb
の実行例:./csv-read.rb {"科目名"=>"基礎プログラミング", "科目コード"=>"154", "担当教員"=>"鳥海三輔", "開講時期"=>"春学期", "概要"=>"プログラミングを通じて問題解決能力を向上を図る。\n" + "本講ではRubyを用い,データ処理の構造と考え方の基礎を学ぶ"} {"科目名"=>"応用プログラミング", "科目コード"=>"254", "担当教員"=>"飯森大和", "開講時期"=>"秋学期", "概要"=> "問題解決を通じてプログラミング能力向上を図る。\n" + "本講ではプログラミング言語Cを通じて,問題解決に最適なアルゴリズムを適用する方策を学ぶ。"} {"科目名"=>"応用もっけ論", "科目コード"=>"39", "担当教員"=>"酒田育造", "開講時期"=>"春学期", "概要"=> "山形県庄内地方でよく使われる「もっけ」には様々な意味があり,他の地域にもこのような多様性を持つ言葉がある。\n" + "その成立ちをふまえて正しくその地域の「もっけ」を理解し,深い交流のきっかけとする。"}
この3つのレコードのうち,「開講時期」が "春学期"
であるものを選択するには以下のようにする。
# csv-read.rbの続きから
db = csv # いったん別の変数にコピー
db = db.select {|row| row["開講時期"] == "春学期"}
ブロック変数 row
は、3度代入され繰り返される。
row["開講時期"]
の値は回ごとに、
"春学期"
,
"秋学期"
,
"春学期"
が得られるため、== "春学期"
で比較して true
が返るのは1行目と3行目である。
なお、selectメソッドによる絞り込みでは,文字列比較だけでなく 正規表現マッチや数値比較も使える。 これらの絞り込みを繰り返すことでどのような検索も可能で、 複合条件の指定もできる。 以下の例は,開講時期と担当教員での絞り込みを正規表現マッチで行ない, 該当するもののみを出力するプログラムである。
CSV.read によるCSVファイル一括読込の標準動作では、 各項目を文字列として代入する。したがって、CSV データの項目中に数値と見なすべきデータがあった場合は、 数値に変換する処理が必要となる。これには以下の2つの方法が考えられる。
表1の履修科目表を CSV.read で読み取ったものから、「科目コード」が100以上200未満のものを選別してみる。 このとき「科目コード」はいずれも必ず整数値が入るものと仮定できるとする。
上記 csv-search.rb の科目検索部分の select による選択を以下のようにすればよい。
db = db.select {|row| # dbの集合から1行ずつ row 変数に取り出して選別 row["科目コード"].to_i >= 100 && row["科目コード"].to_i < 200 # 科目コード(数値) >= 100 かつ 科目コード(数値) < 200 }
以下のように CSV.read に与えるオプションで
converters: :numeric
を与えると数値と解釈できる値は数値化される。
x = CSV.read(csvfile, headers: true, converters: :numeric)
これにより、上記科目コード選択は以下のように書けることになる。
db = db.select {|row| row["科目コード"] >= 100 && row["科目コード"] < 200 }
もちろん、元のCSVデータに数値化できない値があった場合は数値化されず、 数値に対してのみ行なえる演算を施すとエラーになるので注意が必要である。 数値でない項目がある可能性を考える場合は、converters: オプションを用いず、その都度 to_i や to_f を使うのが安全といえる。
複数の値が入った配列 a
を受け取り、各要素を整数に直したものの平均値を求めるメソッド
mean()
を定義せよ。
ja-math.csv
を
CSV.read
で読み取り、国語の得点を配列で得て
puts
で出力するプログラム
ja-list.rb
を作成し、実行例を示せ。
ja-math.csv
から国語の平均点を
求めるプログラム ja-mean.rb
を作成し実行例を示せ。ja-good.rb
を作成し実行例を示せ。