Pythonのメモ帳

numpy, pandas, tensorflow を使いこなすための忘備録

pandasでcsvファイルを読み込む際にあえてカラム(列)を分けない

ファイルの読み込みはいくつか方法があることは前回の記事に書いた。

spcx8.hatenablog.com

 

読み込んだ後の処理を考えると、numpyやpandasで読みたい。でも中身が文字列を含む場合、numpyは使えないのでpandasで読むことになる。

困るのは読み込む対象のファイルの形式が整っていない場合。列数が揃っていない、特に先頭行の列数が少ない場合に読み込みエラーが起こる。

そんな場合は、あえて列を分けずにn行1列のデータフレームとして読み込む方法もある。以下のように引数の delimiter で改行コード'¥n'を指定すればOK。

import pandas as pd

df = pd.read_csv('data.txt', skiprows=0, header=None, delimiter='¥n', index_col=None)

列の分割はデータを読み込んだ後に必要な箇所だけ行う。DataFrameからリストやnumpy.ndarrayへの変換はいくつかの方法がある。それはまた別の機会に。

 

 

 

 

 

csvファイルやexcelファイル(xls, xlsx)を読み込む

 csvファイルやexcelファイル(xls, xlsx)を読み込む場合のコードはいくつかあるのでまとめておく。

 

(1) 標準ライブラリのcsvを使う

import csv

with open('data.txt', 'r', encoding='utf-8') as f:
dataReader = csv.reader(f, lineterminator='\n')
for row in dataReader:
    print(row)

第二引数を'rb'とすればバイナリファイルが扱える。
追加のライブラリが不要。.txt / .csvのどちらも読み込み可能。
空白や文字列があってもOK。

ただし、中身を取り出すのにfor構文が必要で不便。

 

(2) numpy を使う

import numpy as np

data = np.loadtxt('data.txt', delimiter=',', skiprows=0)

np.ndarray型で読み込む。.txt / .csvのどちらも読み込み可能。
ただし,空白や文字列があるとエラーになる。
大抵は先頭行が項目名になってい流けど、その場合は引数をskiprows=1として1行スキップすればOK。

またはgenfromtxt()を使う。空白や文字列があるとnanにしてくれる。

data = np.genfromtxt('data.txt', delimiter=',')

 

 (3) pandasを使う

import pandas as pd

df = pd.read_csv('data.txt', skiprows=0, header=0, index_col=0)

pandas.Dataframe型で読み込む。.txt / .csvのどちらも読み込み可能。
空白や文字列があってもOK。

引数がたくさんあって色々調整できる

header : 項目名に使う行を指定。Noneとしておくと項目名がなくなり、先頭行からデータとして読み込む

index_col:行名として使う列を指定。

その他の引数は省略。 下は関連記事へのリンク。

spcx8.hatenablog.com

 

 

excelファイル(xls, xlsx)を読み込みたい場合は、read_excel()を使う。

df = pd.read_excel('data.xls', skiprows=0, header=0, index_col=2)

 詳しくは別記事で。

 

 

 

 

配列内の各要素に含まれる特定の文字列を置換・削除する方法

文字列の置換はreplace()を使う。

string = 'abcde'
string_new = string.replace('a', 'A')
print(string_new)

出力: 'Abcde'

 

これが使えるのは単独の文字列だけで、配列(各要素が文字列になっている配列)には使えない。

str_list = ['abcde','abcde']
str_list_new = str_list.replace('a', 'A')

これを処理しようとするとエラーになる。
AttributeError
: 'list' object has no attribute 'replace'

 

なので以下のように、配列を一旦単独の文字列に変換し、replace()で置換、のちに配列に戻すという処理をする。

str_list = ['abcde','abcde']
string = ",".join(str_list)
string_new = string.replace('a', 'A')
str_list_new =  string_new.split(",")
print(string_new)

出力: ['Abcde', 'Abcde']

 

ちなみに、このreplace()の2番目の引数を空白('')にしておけば、削除の処理ができる。

string_new = string.replace('a', '')

 

 

 WEB/EC専門の転職支援サービス【サポタント】

 

 

配列から要素をランダムに抽出するための「インデックス」を作る

配列から要素をランダムに抽出するには、(要素の抽出に使う)インデックスをランダムにしておくだけでいい。

random_array = array[random_index]

ここではrandom_indexも配列。

 

以下は関連記事。

spcx8.hatenablog.com

 

順序をランダムにしたインデックス(配列)の作り方は色々ある。比較のため処理時間を測定。いずれも0~99,999の数字を重複なしでランダムに並べた配列を生成している。

import numpy as np

idx = np.array(range(100000))
np.random.shuffle(idx)

 → 所要時間:17 msec

 

idx = np.arange(0, 100000, 1)
np.random.shuffle(idx)

 → 所要時間:2.6 msec

 

idx = np.random.choice(100000, 100000,replace=False)

 → 所要時間:2.0 msec

 

idx = np.random.randint(0,100000,100000)  

 → 所要時間:2.0 msec

 

np.random.randint() とnp.random.choice() が一位タイ。中身の処理は一緒なんだろうか。こうしてみるとnp.random.shuffle()も大して変わらない。

かと言って抽出するもとの配列そのものをランダムにシャッフルしようとすると、その配列のサイズが大きい場合は処理が重くなるのでやらない方がいい。

 

 

 WEB/EC専門の転職支援サービス【サポタント】

 

 

配列から重複なく要素をランダムに抽出し2組に分ける(データセットをシャッフルしつつ2つに分割)

ディープラーニングの前処理としてよくあるのが、データをシャッフルしつつ2つに分ける作業。言い換えると、データセット(配列)から学習データとテストデータを重複なくランダムに取り出す作業。

処理としては、ランダムな配列を作り、それをインデックスとして使ってデータを抽出する。

import numpy as np

num_test  = 10000
num_train = 10000
num_all   = num_train+num_test

id_all   = np.random.choice(num_all, num_all, replace=False)
id_test  = id_all[0:num_test]
id_train = id_all[num_test:num_all]
test_data  = mydataset[id_test]
train_data = mydataset[id_train]

 

下記のようにデータセット自体をシャッフルする方法でも、同様の処理はできる。直感的にわかりやすいけど、処理は遅かった。

import numpy as np

num_test  = 10000
num_train = 10000
num_all   = num_train+num_test

np.random.shuffle(mydataset)
test_data  = mydataset[0:num_test]
train_data = mydataset[num_test:num_all]

 

 

 

 

要素が全てnanの配列を生成をする

 最近書くpythonコードの半分以上はnumpy.ndarrayを使ってる。

numpyがなかったら仕事にならない。それくらいお世話になってる。ただ一つだけ不満というか疑問に思っているのが要素が全てnanの配列を生成をする関数がないこと。要素が全て0の配列を生成する関数 numpy.zeros() があるならnumpy.nans() があってもいいと思うけど、なぜかない。何か理由があるんだろうか。

まあとにかく、そういう関数はないので自前で作るわけで、いつも下のようなやり方をしてる。

import numpy as np
mat = np.zeros([ysize,xsize])
mat[:,:] = np.nan

 

zeros()を使って狙いのサイズの配列を作成したのちに、全要素をnanにする、という処理を行う。(本当はempty()を使った方が計算コストが低いらしいけど)

作りたい配列と同じサイズの配列があったりする場合は、zeros_like() も便利。同じサイズで要素が全て0の配列を作ってくれる。 

mat = np.zeros_like(mat_original)
mat[:,:] = np.nan

 

でもやっぱりnumpy.nans()が欲しい。

 

 

 WEB/EC専門の転職支援サービス【サポタント】

 

 

nan を含む配列の要素をシフトする

配列の要素をシフトさせる関数 shift() のことを前回書いたけど、この関数は配列内の要素にnanが含まれると使えない(出力する配列の要素が全てnanになる)という欠点がある。そして残念ながら今書いてるスクリプトでは配列にnanが入るので、結局自分でコードを書いた。

 前回記事はこちら。

spcx8.hatenablog.com

 

まずは1次元配列をシフトさせるコード。

import numpy as np

def shift_1darray(mat, ys):
    mat_shift = np.zeros_like(mat)
    mat_shift[:,:] = np.nan
    if   ys>0: mat_shift[ys:] = mat[:-ys]
    elif ys<0: mat_shift[:ys] = mat[-ys:]
    else: mat_shift = mat
    return mat_shift

 それから2次元配列をシフトさせるコード。

import numpy as np

def shift_2darray(mat, ys, xs):
    mat_yshift = np.zeros_like(mat)
    mat_yshift[:,:] = np.nan
    if   ys>0: mat_yshift[ys:  ,:] = mat[   :-ys,:]
    elif ys<0: mat_yshift[  :ys,:] = mat[-ys:   ,:]
    else: mat_yshift = mat

    mat_xshift = np.zeros_like(mat)
    mat_xshift[:,:] = np.nan
    if   ys>0: mat_xshift[:,xs:  ] = mat_yshift[:,   :-xs]
    elif ys<0: mat_xshift[:,  :xs] = mat_yshift[:,-xs:   ]
    else: mat_xshift = mat_yshift
    return mat_xshift

 

もっと簡単にできるよ、という方いたらぜひ教えてください。行数減らしたいです。