本日は自然言語処理100本ノックの
第二章UNIXコマンドの基礎をやっていきます。

自然言語処理100本ノック

解説動画はこちら












さて第二章はUNIXコマンドを用いて
やりなさいというのが
問題文に書いてありますので
覚えたほうが良さそうなLINUXコマンドを
挙げておきます。

覚えた方が良さそうなLinuxコマンド
cat (ファイルの内容を表示)
wc (文字や行数のカウント)
tr (文字の置換)
cut (ファイルの行から一部を切り離す)
paste (ファイルを結合する)
head (ファイルの先頭から読む)
tail (ファイルの末尾から読む)
sort (ファイルを並べ替えする)
uniq (重複行を削除する)
fold (テキストを指定した幅で改行して出力する)

こんだけ使いました。
Python言語でやるのも良いのですが
実はUNIX系のコマンドは
自然言語処理では非常に優秀なので
覚えておくと作業が捗ると思います。

この動画はMacOSでやっているので
JupyterNotebook上でもコマンドが使えますが
古いWindowsを用いている人はコマンドが使えないので
コマンドが使える環境で試して下さい。

Google Colab などであれば使えるので
アカウントを持っておくと良いかもしれません。

では問題を解いていきましょう。

第二章ではテキストファイルを用いていきます。
手元にダウンロードしておきましょう。

hightemp.txt

全問共通でこのファイルを使うので
ファイルのパスを指定しておきましょう。

file_path = 'hightemp.txt'



10. 行数のカウント

行数をカウントせよ.
確認にはwcコマンドを用いよ.

最初は行数を数える問題です。
ここら辺は基本ですね。

pythonでの行数カウントは
こんな感じですぐできます。

print(sum(1 for _ in open(file_path)))
24

Linuxコマンドの場合は

wc -l hightemp.txt
24

wcコマンドで行数を数えることができます。



11. タブをスペースに置換

タブ1文字につきスペース1文字に置換せよ.
確認にはsedコマンド,trコマンド,
もしくはexpandコマンドを用いよ.

文字列置換の問題ですね
Pythonでは文字列型の関数であるreplaceで
実現できます。
with open(file_path) as _f:
    for row in _f:
        print(row.replace('\t',' '))
高知県 江川崎 41 2013-08-12

埼玉県 熊谷 40.9 2007-08-16

・・・

Linuxコマンドは1行ですね
cat hightemp.txt | tr $'\t' ' '
高知県 江川崎 41 2013-08-12
埼玉県 熊谷 40.9 2007-08-16

catコマンドでファイル読み込んで
出力した結果を | で繋げて
trコマンドで結果を置換できます。



12. 1列目をcol1.txtに,2列目をcol2.txtに保存

各行の1列目だけを抜き出したものをcol1.txtに,
2列目だけを抜き出したものをcol2.txtとして
ファイルに保存せよ. 確認にはcutコマンドを用いよ.

ファイルに分割する問題ですね

こういった形式のファイルを読み込みする場合
楽な方法としては
Pandasのデータフレームというのが使えます。
import pandas as pd
df = pd.read_table(file_path,header=None)
df.head()
0123
0高知県江川崎41.02013-08-12
1埼玉県熊谷40.92007-08-16
2岐阜県多治見40.92007-08-16
3山形県山形40.81933-07-25
4山梨県甲府40.72013-08-10

タブ区切りのファイルを読み込みする場合は
read_tableというのが使えます。

1行目からデータなのでヘッダー行がありません。
header=Noneで
ヘッダーの指定もしておきましょう。

次にファイルへの分割です。
データフレームの列で抽出し
to_csvでファイル化できます。
df[[0]].to_csv('col1.txt',index=False,header=False)
df[[1]].to_csv('col2.txt',index=False,header=False)
ファイル出力する際はindexとheaderの指定もしておかないと
出力されてしまうので、
Falseを指定して出力されないようにしておきます。

Linuxコマンドの場合は
これをcutコマンドで実現できます。
cat hightemp.txt | cut -f 1 -d $'\t' > p1.txt
cat hightemp.txt | cut -f 2 -d $'\t' > p2.txt

cutコマンドでは区切り文字を指定して
その何列目を取るかという指定ができます。

コマンド > ファイル名

でファイル出力ができます。



13. col1.txtとcol2.txtをマージ

12で作ったcol1.txtとcol2.txtを結合し,
元のファイルの1列目と2列目を
タブ区切りで並べたテキストファイルを作成せよ.
確認にはpasteコマンドを用いよ.

ファイルの結合問題です。

先ほど作ったファイルを連結させてみましょう。
まずはデータフレームに2つのファイルを読み込みします。
Import pandas as pd
df1 = pd.read_table('col1.txt',header=None)
df2 = pd.read_table('col2.txt',header=None)

データフレームの結合は
pd.concat([データフレーム,データフレーム])
で行います。

行列、どちらの方向で結合するのかを指定しないと
いけないのでaxis=1で列方向にくっつけるのを
指定します。
df3 = pd.concat([df1,df2],axis=1)
df3.to_csv('col3.txt',index=False,header=False,sep='\t')
ファイル出力はto_csvですが
区切り文字を指定しておかないと
タブ区切りにはならないのでsep=区切り文字
で区切り文字を指定しましょう。

Linuxコマンドの場合は
paste -d '\t' col1.txt col2.txt > p12.txt
pasteコマンドでファイル連結の結果出力ができます。
その場合どの区切り文字で連結するかを指定し
最後に結果をファイル出力をします。



14. 先頭からN行を出力

自然数Nをコマンドライン引数などの手段で受け取り,
入力のうち先頭のN行だけを表示せよ.
確認にはheadコマンドを用いよ.

これもデータフレームで簡単に操作できます。
データフレーム変数.head(行数)で
先頭行から抽出できます。

N = int(input())
df = pd.read_table(file_path,header=None)
df.head(N)
2
0123
0高知県江川崎41.02013-08-12
1埼玉県熊谷40.92007-08-16
input()で入力を受け取ることができます。
int()で整数値に変換しています。

Linuxコマンドも同様のheadコマンドで
先頭行から出力できます。
head -3 hightemp.txt
高知県	江川崎	41	2013-08-12
埼玉県	熊谷	40.9	2007-08-16
岐阜県	多治見	40.9	2007-08-16


15. 末尾のN行を出力

自然数Nをコマンドライン引数などの手段で受け取り,
入力のうち末尾のN行だけを表示せよ.
確認にはtailコマンドを用いよ.

これは14問目がtailに変わるだけです。

tailコマンドは末尾から出力できます。



16. ファイルをN分割する

自然数Nをコマンドライン引数などの手段で受け取り,
入力のファイルを行単位でN分割せよ.
同様の処理をsplitコマンドで実現せよ.


ファイル分割ですがいろいろやり方があるので
1例を紹介します。

numpyのsplitを用いて
データフレームを分割します。
import pandas as pd
import numpy as np
N = int(input())
df = pd.read_table(file_path,header=None)
chunks = np.split(df,N)
for i in range(N):
    print(chunks[i])
3
      0     1     2           3
0   高知県   江川崎  41.0  2013-08-12
1   埼玉県    熊谷  40.9  2007-08-16
2   岐阜県   多治見  40.9  2007-08-16
3   山形県    山形  40.8  1933-07-25
4   山梨県    甲府  40.7  2013-08-10
5  和歌山県  かつらぎ  40.6  1994-08-08
6   静岡県    天竜  40.6  1994-08-04
7   山梨県    勝沼  40.5  2013-08-10
      0    1     2           3
8   埼玉県   越谷  40.4  2007-08-16
9   群馬県   館林  40.3  2007-08-16

分割した結果を表示しています。
ファイル出力をしたければ
繰り返しの中でファイル出力を行い
ファイル名を動的に変えれば実現できると思います。



17. 1列目の文字列の異なり

1列目の文字列の種類(異なる文字列の集合)を求めよ.
確認にはsort, uniqコマンドを用いよ.

ユニークカウントの問題です。

さて、こっからはテクニックが必要かなーと思います。
まずはPythonの方から

データフレームに読み込んだ後は
value_countsで
列の値のユニークカウントができます。

1列目を指定してvalue_countsの結果の
index値の個数を求めています。

import pandas as pd
df = pd.read_table(file_path,header=None)
print(len(df[0].value_counts().index))
12

Linuxコマンドでは
まずcutでタブ区切りにした結果を sort し
uniq でユニークカウントします。
cat hightemp.txt |  cut -f 1 -d $'\t' | sort | uniq -c | sort -nr
   3 群馬県
   3 山梨県
   3 山形県
   3 埼玉県
   2 静岡県
   2 愛知県
   2 岐阜県
   2 千葉県
   1 和歌山県
   1 高知県
   1 愛媛県
   1 大阪府


個数だけ出したければwcコマンドを繋げましょう。
cat hightemp.txt |  cut -f 1 -d $'\t' | sort | uniq -c | sort -nr | wc -l
12


18. 各行を3コラム目の数値の降順にソート

各行を3コラム目の数値の逆順で整列せよ
確認にはsortコマンドを用いよ

並び替えの問題ですね。

データフレームではsort_valuesで
並び替えをすることができます。

ソートでは昇順と降順があるので
ascending=Trueで昇順、Falseで降順です。

df = pd.read_table(file_path,header=None)
df.sort_values(2,ascending=True)
0123
23愛知県名古屋39.91942-08-02
21山梨県大月39.91990-07-19
20大阪府豊中39.91994-08-08

データフレームではデータの型があります。
数値型であれば数値の大小での並び替えになります。

Linuxコマンドではsortコマンドで並び替えできます。
並び替えの対象列を指定してます。
!sort -n -k 3 hightemp.txt
山形県	鶴岡	39.9	1978-08-03
山梨県	大月	39.9	1990-07-19
大阪府	豊中	39.9	1994-08-08


19. 各行の1コラム目の文字列の出現頻度を求め,
出現頻度の高い順に並べる


各行の1列目の文字列の出現頻度を求め,
その高い順に並べて表示せよ.
確認にはcut, uniq, sortコマンドを用いよ.

17問目と似ていますが
17問目は各行の値でのユニークカウント
今度はそれぞれの文字でのユニークカウントですね。

まずは1列目の文字を全部抜き出してみましょう。

text = []
with open(file_path) as _f:
    for row in _f:
        text.append(row.split('\t')[0])
texts = ''.join(text)
with 構文でファイル読み込みして
1列目だけをリスト型に格納し
それをjoinで繋げて1つの文字列にします。

for文では文字列型は
1文字ずつ処理できるので
1文字ずつ集計します。

集計は辞書型などで行うことができます。
res = {}
for t in texts:
    if t in res:
        res[t]+=1
    else:
        res[t]=1
最後に辞書型の結果を並び替えて出力です。
for k,v in sorted(res.items(),key=lambda x:x[1],reverse=True):
    print(k,v)
県 23
山 7
知 3
埼 3


結構長くなりましたね。

Linuxコマンドをみてみましょう。

17問目の答えでは1行ずつ処理していましたので
そこに1つコマンドを足します。
cat hightemp.txt |  cut -f 1 -d $'\t' | fold -w 1 | sort | uniq -c | sort -nr
  24 
  23 県
   7 山
   3 馬

fold コマンドは指定した文字数で改行させるコマンドです。
これを用いて1行の文字列を1文字ずつ複数行に変換し
それをユニークカウントしています。


はい
第二章はこれで終わりです。

Linuxコマンドは
Macでは標準で使用できますし
開発現場では普通に使用していると思います。

ログファイルの操作や中身の確認などでも
威力を発揮しますし、1行で済んだりして
とても効率が良いです。

Pythonなどと並行して
覚えておくと自然言語処理がスムーズになるかと思いますので
覚えておいて損はないと思います。

今回はここまでです
それでは。