始める前に
今回は第2章「UNIXコマンド」を解説していきます。
UNIX(ユニックス)とは、1969年にアメリカのAT&Tベル研究所で開発されたマルチユーザー・マルチタスク対応のオペレーティングシステム(OS)です。
Pythonはpandasというライブラリではなく、with open…で標準ライブラリを使った実装をしていきます。
pandasも後に追加する予定です。
第二章
Unixのコマンドの実装例とPythonでの実装例の2つを第二章では掲載します。
ファイルの読み込み
Unix
cat popular-names.txt
catコマンドを使うことで、ファイルの中身を出力することができます。
Python
with open("popular-names.txt") as f:
lines = f.read()
print(lines)
with open(ファイル名) as fでファイルを開いてfという変数に格納します。
withがなくても、openでファイルを開くことができますが、その際f.close()のようにファイルを閉じる必要があります。
つまり、withがあるおかげで自動でファイルを閉じてくれます。
read()はファイルの中身を文字列として読み込むことができます。
他にも、一行のみを読み込むreadline()や一行ずつの要素をリスト化できるrealines()があります。
10. 行数のカウント
Unix
wc -l popular-names.txt
wcはword count(ワードカウント) の略です。
ファイルの 行数・単語数・文字数 を数えるために使われます。
行数のみは-l、単語数のみは-w、文字数のみは-cのようにオプションで指定することができます。
Python
# popular-names.txt の行数を数える
with open("popular-names.txt") as f:
lines = f.readlines()
print(len(lines))
すべての行をリスト化して、len()を使って数えることで行数を調べることができます。
11. 先頭からN行を出力
Unix
head -10 popular-names.txt
先頭からの数行を表示するにはheadコマンドを使用します。
オプションに数字を指定することで、その数の行を表示します。
Python
with open("popular-names.txt") as f:
lines = f.readlines()
for i in range(10):
print(lines[i])
readlinesでリスト化の後、for文を使用して先頭の10行を表示します。
12. 末尾のN行を出力
Unix
tail -10 popular-names.txt
tailを使用することで末尾から指定オプション数表示させることができます。
Python
with open("popular-names.txt") as f:
lines = f.readlines()
for i in range(-10, 0):
print(lines[i])
11とは対照的に、末尾のデータを取得したいため、負の値をrange関数により取得する必要があります。
13. タブをスペースに置換
Unix
sedコマンド
head -10 popular-names.txt | sed 's/\t/ /g'
sed は「文字列の置換」を行うコマンドで、sed ‘s/置換前/置換後/オプション’/\t(タブ)/ (空白)/g(すべて) は「すべてのタブ文字をスペースに置換する」意味です。
trコマンド
head -10 popular-names.txt | tr '\t' ' '
tr は「文字の置換」コマンドです。\t(タブ)を ' '(スペース)に変換します
expandコマンド
head -10 popular-names.txt | expand -t 1
expand は、タブをスペースに展開するコマンドです。
-t 1 はタブ1文字をスペース1文字に変換します
Python
with open("popular-names.txt") as f:
lines = f.readlines()
for i in range(10):
print(lines[i].replace('\t',' '))
置換する関数にreplace(変換前,変換後)があります。
これを11で使った先頭10行を表示するコードに応用させます。
14. 1列目を出力
Unix
cut -f 1 popular-names.txt | head -n 10
cutコマンドは特定の列だけを抜き出すのにつかわれます。
-f 数 でその数の列に対して、抜き出します。
標準ではタブが区切り文字となり列を決定しています。(区切り文字を変えたいときは-d)
Python
with open("popular-names.txt") as f:
lines = f.readlines()
for i in range(10):
print(lines[i].split()[0])
先頭10行を取り出し、splitでさらにリストに分割します。
splitの区切り文字は空白文字(スペース・タブ・改行)です。
15. ファイルをN分割する
Unix
mkdir split-data
split -n 10 "popular-names.txt" "./split-data/"
split -n 数 入力ファイル 出力先をもとにテキストを10分割し指定のディレクトにファイルを出力していきます。
Python
import os
# 元ファイル読み込み
with open("popular-names.txt") as f:
lines = f.readlines()
N = 10 # 分割数
num = len(lines) // N # 各ファイルの行数
# 保存先フォルダを作成
os.makedirs("split-data", exist_ok=True)
# ファイルを分割して書き出し
for idx in range(N):
start = idx * num
end = (idx + 1) * num if idx < N - 1 else len(lines)
with open(f"split-data/output_{idx + 1}.txt", "w") as f:
f.writelines(lines[start:end])
ファイルを分割して書き出しの部分が少し複雑になっています。
for文でそれぞれ分割時のファイルに書き込みます。
具体的に、N=10,num=,278,idx=0の時を考えます。これが0のとき、start=0*278=0,end=(0+1)*278となります。そして、linesをスライスして、ファイルに書き込みます。
今 end = (idx + 1) * num if idx < N – 1 else len(lines)のifを無視してしまいましたが、これは最後の分割ファイルの時に役に立ちます。
100個の行数で分割するとき、最後の分割ファイルでは901から984行で100個に到達しない場合があります。つまり、スライスはlines[900:984]でなければなりません。
もし if を省略して単純に end = (idx + 1) * num とすると、
最後のファイルで lines[900:1000] のようにスライスしようとして、
実際の行数より多く指定してしまう可能性があります(エラーにはならないが、無駄な処理になる)。
これを実装するためにもif idx < N-1という条件を付けています。
16. ランダムに各行を並び替える
Unix
shuf popular-names.txt
シャッフル(shuffle)するにはshuf コマンドを使うことで簡単にシャッフルすることができます。
Python
import random
with open("popular-names.txt") as f:
lines = f.readlines()
random_list=random.sample(lines,len(lines))
for x in random_list:
print(x)
random.sample(list,数)で取り出したリストをランダム化することができます。
17. 1列目の文字列の異なり
Unix
!cut -f 1 popular-names.txt | sort | uniq
14番のcut -f 1 popular-names.txt で一列目を取得します。
その後、sortによりa-z順で並び替えて、uniqで重複をまとめて1行にします。
Python
with open("popular-names.txt") as f:
lines = f.readlines()
# 1列目を取り出す
ans = [line.split()[0] for line in lines]
# 重複を除去してからソート
unique = sorted(set(ans))
# 出力
for x in unique:
print(x)
14のPythonを使い、一列目を取り出します。
そして、sorted()で並び替えます。重複の消去はリストから集合にすることでそれらを取り除くことができます。
18. 各行の1列目の文字列の出現頻度を求め、出現頻度の高い順に並べる
Unix
cut -f 1 popular-names.txt | sort | uniq -c | sort -nr
cut -f 1 popular-names.txt | sort で、一列目を取得し並び替えます。
次に、uniqは重複の行をひとまとめにします。このとき、-cオプションを付けることで同時にその数も数えてくれます。
最後に、頻出順に並べる必要がありますが、sortに-n (number)と-r (reverse)をつけることで数値を対象に、数が大い順にsortします。
Python
from collections import Counter
with open("popular-names.txt") as f:
lines = f.readlines()
# 1列目を取り出す
ans = [line.split()[0] for line in lines]
# 出現回数をカウント
counter = Counter(ans)
# 出現頻度の高い順に並べる
for name, count in counter.most_common():
print(count, name)
Counter()とは、要素の出現回数を数えて、Couterというオブジェクトで内部にその辞書データを保持します。
いくつかメソッドがありますが、今回はmost_common()を使い出現頻度の高いものをリスト([(要素, 出現回数), …])を返します。
19. 3列目の数値の降順に各行を並び替える
Unix
!sort -k3 -nr popular-names.txt
-k数でその列を基準にsortを行っていきます。
-nrは-n(number)と -r(reverse)数として降順に並び替えます。
Python
# ファイル読み込み
with open("popular-names.txt") as f:
lines = f.readlines()
def key_select(x):
return int(x.split()[2])
# 3列目の数値をキーにして降順ソート
sorted_lines = sorted(lines, key=key_select, reverse=True)
for line in sorted_lines:
print(line)
sorted関数にkeyという引数を渡すことでこれを元に並び替えます。
keyを選ぶうえで3列目の前処理をする必要があります。それを行うには、keyには値ではなくメソッドを入れて、それぞれの行に対して処理を行います。
このメソッドの作り方は、まず引数として要素をxとして受け取ります。そして、この要素は行であるため、これを空白区切りのリストに、3列目の値を整数値で返すような関数を作ります。
こうすることで、sortedが自動的にkeyを探し、降順に並び替えてくれるのです。
まとめ
今回は、論理性よりもUnixのコマンドの知識やファイルの書き込み、開き方などデータサイエンスで必須になる基礎知識が多くありました。
これからの時代はpandasなどの便利なツールが台頭し続けるため、with openを知らなくても良いように見えますが、この読み込み・書き込みがどの言語でも雛形となっているので、頭の片隅にでも置いておくのが良いですかね。
次は「第3章: 正規表現」となりますので、今後とも頑張りましょう。


コメント