自然言語処理の問題集として有名な言語処理100本ノックの2020年版の解答例です。 この記事では、以下の第1章から第10章のうち、「第2章: UNIXコマンド」を解いてみた結果をまとめています。
- 第1章: 準備運動
- 第2章: UNIXコマンド
- 第3章: 正規表現
- 第4章: 形態素解析
- 第5章: 係り受け解析
- 第6章: 機械学習
- 第7章: 単語ベクトル
- 第8章: ニューラルネット
- 第9章: RNNとCNN
- 第10章: 機械翻訳
環境設定
コードの実行はGoogle Colaboratoryで行います。 以降の解答の実行結果をすべて含むノートブックは、以下のリンクから直接参照することができます。
第2章: UNIXコマンド
popular-names.txtは,アメリカで生まれた赤ちゃんの「名前」「性別」「人数」「年」をタブ区切り形式で格納したファイルである.以下の処理を行うプログラムを作成し,popular-names.txtを入力ファイルとして実行せよ.さらに,同様の処理をUNIXコマンドでも実行し,プログラムの実行結果を確認せよ.
指定のデータをダウンロードします。 Google Colaboratoryのセル上で下記のコマンドを実行すると、現在のディレクトリに対象のテキストファイルがダウンロードされます。
!wget https://nlp100.github.io/data/popular-names.txt
10. 行数のカウント
行数をカウントせよ.確認にはwcコマンドを用いよ.
本章の趣旨とは異なる気がしますが、今回は実際のデータ分析のシーンで出会うことが多いと思われるpandasを利用します。 まずは、データフレームとして読み込んでから各問の処理を行っていきます。また、問題文の指示に従い、コマンドでの結果の確認も行っています。
import pandas as pd df = pd.read_table('./popular-names.txt', header=None, sep='\t', names=['name', 'sex', 'number', 'year']) len(df)
--- 出力 --- 2780
wcはテキストファイルの行数や単語数を数えるためのコマンドです。ここでは-l
を指定して、改行数(=行数)をカウントしています。
# 確認 !wc -l ./popular-names.txt
--- 出力 --- 2780 ./popular-names.txt
11. タブをスペースに置換
タブ1文字につきスペース1文字に置換せよ.確認にはsedコマンド,trコマンド,もしくはexpandコマンドを用いよ.
確認のみ行います。
sedコマンドを利用します。-e
は処理内容の指定、最初のs/
は正規表現を用いるための指示で、最後の/g
は正規表現にマッチしたすべての文字列を置換することを意味します。そして、\t/ /
でタブをスペースに変換することを表します(変換前 / 変換後)。また、head -n 5
で最初の5行を表示しています。間の|
はsed
の出力をhead
に渡すための記号です。
!sed -e 's/\t/ /g' ./popular-names.txt | head -n 5
--- 出力 --- Mary F 7065 1880 Anna F 2604 1880 Emma F 2003 1880 Elizabeth F 1939 1880 Minnie F 1746 1880
12. 1列目をcol1.txtに,2列目をcol2.txtに保存
各行の1列目だけを抜き出したものをcol1.txtに,2列目だけを抜き出したものをcol2.txtとしてファイルに保存せよ.確認にはcutコマンドを用いよ.
col1 = df['name'] col1.to_csv('./col1.txt', index=False) print(col1.head()) col2 = df['sex'] col2.to_csv('./col2.txt', index=False) print(col2.head())
--- 出力 --- 0 Mary 1 Anna 2 Emma 3 Elizabeth 4 Minnie Name: name, dtype: object 0 F 1 F 2 F 3 F 4 F Name: sex, dtype: object
# 確認 !cut -f 1 ./popular-names.txt | head -n 5 # popular-names.txtの1列目の最初の5行
--- 出力 --- Mary Anna Emma Elizabeth Minnie
!cut -f 2 ./popular-names.txt | head -n 5 # popular-names.txtの2列目の最初の5行
--- 出力 --- F F F F F
13. col1.txtとcol2.txtをマージ
12で作ったcol1.txtとcol2.txtを結合し,元のファイルの1列目と2列目をタブ区切りで並べたテキストファイルを作成せよ.確認にはpasteコマンドを用いよ.
col1 = pd.read_table('./col1.txt') col2 = pd.read_table('./col2.txt') merged_1_2 = pd.concat([col1, col2], axis=1) merged_1_2.to_csv('./merged_1_2.txt', sep='\t', index=False) merged_1_2.head()
--- 出力 --- name sex 0 Mary F 1 Anna F 2 Emma F 3 Elizabeth F 4 Minnie F
# 確認 !paste ./col1_chk.txt ./col2_chk.txt | head -n 5
--- 出力 --- Mary F Anna F Emma F Elizabeth F Minnie F
14. 先頭からN行を出力
自然数Nをコマンドライン引数などの手段で受け取り,入力のうち先頭のN行だけを表示せよ.確認にはheadコマンドを用いよ.
N = input('>> ') df.head(int(N))
--- 出力 --- name sex number year 1340 Linda F 99689 1947 1360 Linda F 96211 1948 1350 James M 94757 1947 1550 Michael M 92704 1957 1351 Robert M 91640 1947
# 確認 !head -n 5 ./popular-names.txt
--- 出力 --- Mary F 7065 1880 Anna F 2604 1880 Emma F 2003 1880 Elizabeth F 1939 1880 Minnie F 1746 1880
15. 末尾のN行を出力
自然数Nをコマンドライン引数などの手段で受け取り,入力のうち末尾のN行だけを表示せよ.確認にはtailコマンドを用いよ.
N = input('>> ') df.tail(int(N))
--- 出力 --- name sex number year 2775 Benjamin M 13381 2018 2776 Elijah M 12886 2018 2777 Lucas M 12585 2018 2778 Mason M 12435 2018 2779 Logan M 12352 2018
# 確認 !tail -n 5 ./popular-names.txt
--- 出力 --- Benjamin M 13381 2018 Elijah M 12886 2018 Lucas M 12585 2018 Mason M 12435 2018 Logan M 12352 2018
16. ファイルをN分割する
自然数Nをコマンドライン引数などの手段で受け取り,入力のファイルを行単位でN分割せよ.同様の処理をsplitコマンドで実現せよ.
いろいろなやり方があると思いますが、ここではレコードの通番に対して、N分位点を算出するqcutを適用することでファイルをN分割するフラグを付与しています。
def split_file(N): tmp = df.reset_index(drop=False) df_cut = pd.qcut(tmp.index, N, labels=[i for i in range(N)]) df_cut = pd.concat([df, pd.Series(df_cut, name='sp')], axis=1) return df_cut N = input('>> ') df_cut = split_file(int(N)) df_cut['sp'].value_counts()
--- 出力 --- 9 278 8 278 7 278 6 278 5 278 4 278 3 278 2 278 1 278 0 278 Name: sp, dtype: int64
df_cut.head()
--- 出力 --- name sex number year sp 0 Mary F 7065 1880 0 1 Anna F 2604 1880 0 2 Emma F 2003 1880 0 3 Elizabeth F 1939 1880 0 4 Minnie F 1746 1880 0
# コマンドによる分割 !split -l 200 -d ./popular-names.txt sp
17. 1列目の文字列の異なり
1列目の文字列の種類(異なる文字列の集合)を求めよ.確認にはcut, sort, uniqコマンドを用いよ.
指定の列で重複を除き、行数をカウントしています。
len(df.drop_duplicates(subset='name'))
--- 出力 --- 136
# 確認 !cut -f 1 ./popular-names.txt | sort | uniq | wc -l
--- 出力 --- 136
18. 各行を3コラム目の数値の降順にソート
各行を3コラム目の数値の逆順で整列せよ(注意: 各行の内容は変更せずに並び替えよ).確認にはsortコマンドを用いよ(この問題はコマンドで実行した時の結果と合わなくてもよい).
df.sort_values(by='number', ascending=False, inplace=True) df.head()
--- 出力 --- name sex number year 1340 Linda F 99689 1947 1360 Linda F 96211 1948 1350 James M 94757 1947 1550 Michael M 92704 1957 1351 Robert M 91640 1947
# 確認 !cat ./popular-names.txt | sort -rnk 3 | head -n 5
--- 出力 --- Linda F 99689 1947 Linda F 96211 1948 James M 94757 1947 Michael M 92704 1957 Robert M 91640 1947
19. 各行の1コラム目の文字列の出現頻度を求め,出現頻度の高い順に並べる
各行の1列目の文字列の出現頻度を求め,その高い順に並べて表示せよ.確認にはcut, uniq, sortコマンドを用いよ.
df['name'].value_counts()
--- 出力 --- James 118 William 111 Robert 108 John 108 Mary 92 ... Crystal 1 Rachel 1 Scott 1 Lucas 1 Carolyn 1 Name: name, Length: 136, dtype: int64
# 確認 !cut -f 1 ./popular-names.txt | sort | uniq -c | sort -rn
--- 出力 --- 118 James 111 William 108 Robert 108 John 92 Mary
理解を深めるためのオススメ教材
全100問の解答はこちら