言語処理100本ノック 2020 第2章 前半
いきなり5月になって暑くなった。冷やし中華はじめたい気分。
第2章 UNIXコマンドの前半(10-14まで)解説書きます。
最初はwith openして書いてたけど、pandasに慣れたかったのでpandasを使ってみた。
目次
- 10. 行数のカウント
- 11. タブをスペースに置換
- 12. 1列目をcol1.txtに,2列目をcol2.txtに保存
- 13. col1.txtとcol2.txtをマージ
- 14. 先頭からN行を出力
この章で使うファイル
タブ区切りでアメリカの赤ちゃんの名前、性別、人数、生まれた年が記録された
テキストファイル。
$ head popular-names.txt Mary F 7065 1880 Anna F 2604 1880 Emma F 2003 1880 Elizabeth F 1939 1880 Minnie F 1746 1880 Margaret F 1578 1880 Ida F 1472 1880 Alice F 1414 1880 Bertha F 1320 1880 Sarah F 1288 1880
10. 行数のカウント
pandasでやると圧倒的に楽だった。
ポイント
- pandasでのファイル読み込みは read_csv を使う
- CSVファイルでなくてもOK
sep
で区切り文字、ヘッダがない場合はheader=None
を指定
- pandas.DataFrameの行数を数える時は len(df) を使う
解答コマンド
wcコマンドの-l
オプションで行数を数えられる
wc -l popular-names.txt
解答スクリプト
# coding:utf-8 import pandas as pd df = pd.read_csv('./popular-names.txt', sep='\t', header=None) print(df) print(len(df))
11. タブをスペースに置換
一行ずつ読み込んで、str.replace( '\t', ' ' )
もいいけど、ここはpandasで。
ポイント
- pandasでのファイル書き込みは to_csv を使う
- これもCSVファイルでなくてもOK
sep
で区切り文字、ヘッダがない場合はheader=None
を指定- インデックス(行番号)不要の場合、
index=None
を指定
- pandas.DataFrameの行数を数える時は len(df) を使う
解答コマンド
catコマンド+trコマンドでやる場合はパイプで連結
私の環境はmacで、sedでtabの表現が'\t'
では効かず、Tabをコピペしないと認識しない。
本筋とそれるので詳しくは参考記事で。
cat popular-names.txt | tr '\t' ' ' > out.txt
タブを空白文字に変換するexpandコマンドでも実現可能。
-t
オプションはtabを何文字の空白で置換するか指定する。
今回は1文字で揃えるように指定した。
デフォルトは8文字らしい。
expand -t 1 popular-names.txt > out.txt
解答スクリプト
新しくファイルを書き出す時に、デリミタをスペース1つにしてあげるだけ。
# coding:utf-8 import pandas as pd path = './popular-names.txt' df = pd.read_csv(path, sep='\t', header=None) print(df) df.to_csv('./out.txt', sep=' ', header=None, index=None)
参考記事
12. 1列目をcol1.txtに,2列目をcol2.txtに保存
DataFrameにして列を切り出してあげる問題。
もうここから愚直に実装する気をなくす。
ポイント
- cutコマンドのデフォルトのデリミタはTab
- pandas.DataFrameでカラム名を入れたい時、 df.columns = [カラム名のリスト] を使う
- pandas.DataFrameでカラム名を指定し列を参照する時、 df['カラム名'] と書く
解答コマンド
cutコマンドでそれぞれの列を別々のファイルに書き出すだけ。
-f
でフィールド(列)を指定する。ただし、1からスタート
cut -f 1 popular-names.txt cut -f 2 popular-names.txt
解答スクリプト
なくてもよかったけど、カラム名を入れてあげた。
カラム名入れないままやる場合、例えば1列目がほしい時はdf[0]
になる
# coding:utf-8 import pandas as pd path = './popular-names.txt' df = pd.read_csv(path, sep='\t', header=None) df.columns = ['Name', 'Sex', 'Count', 'Year'] col1 = df['Name'] col2 = df['Sex'] col1.to_csv('./col1.txt', header=None, index=None) col2.to_csv('./col2.txt', header=None, index=None)
13. col1.txtとcol2.txtをマージ
分解したら戻す。鉄則。(ちがう)
ポイント
- pandas.DataFrame同士の結合は、 df.concat([df1, df2]) を使う
- 縦方向に連結(UNION文イメージ)だと
df.concat([df1, df2])
でOK - 列同士の連結だと、
df.concat([df1, df2], axis=1)
のようにaxis=1 がオプションで必要
- 縦方向に連結(UNION文イメージ)だと
解答コマンド
pasteコマンドで2つのファイルをくっつけて表示できる。
paste col1.txt col2.txt
解答スクリプト
# coding:utf-8 import pandas as pd p1 = './col1.txt' p2 = './col2.txt' df1 = pd.read_csv(p1, sep='\t', header=None) df2 = pd.read_csv(p2, sep='\t', header=None) df = pd.concat([df1, df2], axis=1) df.to_csv('merge.txt', sep='\t', header=None)
参考記事
14. 先頭からN行を出力
第1章 07で出てきた argparse
再登場。引数を受け取るところを関数化しておく。
ポイント
- argparseで引数を取った時、デフォルトはstr型
- pandas.DataFrameから先頭のn行を取り出す時、スライスdf[:n]か df.head(n) を使う
解答コマンド
最初の3行だけほしい時の場合。
headコマンドは-n
オプションで表示したい行数を指定できる。
デフォルトは先頭10行が表示される。
head -n 3 popular-names.txt
解答スクリプト
引数はstr型で返ってくるので、intにキャストする。(最初それに気づかず地味にエラー出した人)
# coding:utf-8 import argparse import pandas as pd def arg(): parser = argparse.ArgumentParser(description='自然数') parser.add_argument('n', help='num') args = parser.parse_args() return(args.n) def main(): path = './popular-names.txt' n = int(arg()) df = pd.read_csv(path, sep='\t', header=None) print(df[:n]) # print(df.head(n)) if __name__ == "__main__": main()