2008年6月9日月曜日

Python, Ruby で簡単なテキスト処理 (2) - 表からのデータの抽出

Python で簡単なテキスト処理 (1) - 特定の単語が含む行を抽出する」の続き。

1. 表から、特定の列を抽出したい

今回は、実際に存在するデータから、特定の列を抽出をしてみる。

抽出対象のデータは、気象庁で公表されている気象データ。

気象庁の過去のデータを見ると、日毎、時間毎に、いくつかの観測対象のデータが載せられている。

上記のデータは、

「 2008 年 1 月の東京の日ごと」

の気象データが掲載されているページ。この中から、特定の観測対象のデータのみを抽出したい。ただし、抽出するとき、抽出データを後で表計算に貼り付けるため、「タブ区切り」にすること。

対象の項目は、「現地の気圧」と「平均気温」のとする。

 

2. データをブラウザからエディタに貼りつける

例えば、東京の2008/1/1 ~ 1/5 までのデータを、ブラウザ上で選択し、エディタに貼り付けると、次のようになる。

1
 998.7 1003.1 -- -- -- 6.0 10.9 1.8 36 16 2.6 4.8 北西 9.4 北西 6.9 -- -- 晴後一時曇 快晴
2
 1007.2 1011.7 -- -- -- 6.2 11.2 1.1 35 19 3.1 6.6 北西 13.3 北西 9.1 -- -- 快晴 快晴
3
 1011.6 1016.1 -- -- -- 5.9 10.4 1.5 45 29 1.8 3.6 北北西 6.4 南南東 9.1 -- -- 晴 快晴
4
 1014.6 1019.1 -- -- -- 7.0 12.1 2.7 43 24 2.5 5.0 北西 9.4 北北西 9.0 -- -- 快晴 晴後曇
5
 1014.0 1018.6 0.0 0.0 0.0 6.0 8.7 4.0 53 39 2.0 3.8 北西 6.1 西北西 3.7 -- 

これを文字列として変数に代入し、処理をする。

 

3. Python の場合

data = ur"""ここに上記のデータを貼り付ける"""

import re

# 改行でデータを分割
for line in data.split('\n'):
    # 日付の行は飛ばす
    if re.search('^\d{1,2}$', line): continue
    # 空白でレコードを分割
    rec = line.split()
    # 必要な観測データのみ、タブ区切りでレコードを出力
    print rec[0], '\t', rec[5]

以下を参考。

 

リスト内包表記を使う場合

追記(2008.6.10): リスト内包表記を利用し、後述する Ruby のコードを真似てみる。

data = ur"""ここにデータを貼り付ける"""

for line in [line for line in
        data.split('\n') if not re.search('^\d{1,2}$', line)]:
    for i,v in enumerate(line.split()):
        if   i == 0: print v, "\t",
        elif i == 5: print v

うーん、読みにくい... ^^;

enumerate() をリスト内包表記の中に入れてやれば、

data = ur"""ここにデータを貼り付ける"""

for a,b in [filter(lambda (i,v): i in [0, 5] , enumerate(line.split()))
            for line in
                data.split('\n') if not re.search('^\d{1,2}$', line)]:
    print a[1], "\t", b[1]

filter() をリスト内包表記に変更すると、

data = ur"""ここにデータを貼り付ける"""

for rec in [[v for i,v in enumerate(line.split()) if i in [0,5]]
            for line in
                data.split('\n') if not re.search('^\d{1,2}$', line)]:
    print rec[0], "\t", rec[1]

enumerate() した後にフィルタしたものを [] で囲むのを忘れずに。

ついでに、reduce() を使うなら、(参考: jijixi's diary - リストの扱いあれこれ)

data = ur"""ここにデータを貼り付ける"""

for rec in [reduce(lambda r,(i,v): r + [v]  if i in [0,5] else r,
                    enumerate(line.split()),
                    [])
            for line in
                data.split('\n') if not re.search('^\d{1,2}$', line)]:
    print rec[0], "\t", rec[1]

reduce() 内の lambda における v を [] で囲むのを忘れずに。

しかし、慣れていないので読みにくい... ^^;

 

4. Ruby の場合

Ruby の場合、正規表現の書き方がシンプルでいい ^^

DATA.each do |line|
  next if line =~ /^\d{1,2}$/
  rec = line.split
  print rec[0], "\t", rec[5],"\n"
end

__END__
ここにデータを貼り付ける

追記(2008.6.10): 最初に不必要なデータを削除しておくやり方。

DATA.reject{|line|
  line =~ /^\d{1,2}$/
}.each{|line|
  line.split.each_with_index{|v,i|
    case i
    when 0
      print v, "\t"
    when 5
      puts  v
    end
  }
}

__END__
ここにデータを貼り付ける

6. 関連記事