簡単なpandasデータの作り方
>>> import pandas as pd >>> twod_array = [[0,1,2],[3,4,5],[6,7,8]] >>> data =pd.DataFrame(twod_array) >>> data 0 1 2 0 0 1 2 1 3 4 5 2 6 7 8 >>> data[:] 0 1 2 0 0 1 2 1 3 4 5 2 6 7 8 >>> data[:][:] 0 1 2 0 0 1 2 1 3 4 5 2 6 7 8
:
で全要素を選択する
>>> data[3][:] Traceback (most recent call last): ~~~ KeyError: 3
存在しない行(名)や列(名)の指定でKeyError
が起きる.
データフレーム
- pd.Series :1次元配列に対応した表形式データのオブジェクト
- pd.DataFrame:2次元配列に対応した表形式データのオブジェクト←pd.Seriesの上位互換
基本的に使い方や使えるメソッドは同じ.ただし,扱える次元数に注意する.
value_counts()
はpd.Seriesで使えるメソッド.pd.DataFramにはないので,pd.DataFramをスプライシングしてpd.Seriesにしたら使える.
スライシングの方法
例
>>> data[0] 0 0 1 3 2 6 Name: 0, dtype: int64 >>> data[0][1] 3 >>> data[0:2+1:2] 0 1 2 0 0 1 2 2 6 7 8 >>> data[0:2+1:2][:] 0 1 2 0 0 1 2 2 6 7 8 >>> data[:][0:2+1:2] 0 1 2 0 0 1 2 2 6 7 8 >>> data[0][0:2+1:2] 0 0 2 6 Name: 0, dtype: int64
まとめ
# 列のスライス data[start:end+1:stime][:] # 行のスライス data[:][start:end+1:stime]
- start :開始
- end :終了
- stime:区切り間隔
いくつかのデータの統合
>>> no4ch = pd.DataFrame([0,1,2,3]) >>> no4ch 0 0 0 1 1 2 2 3 3 >>> no6ch = pd.DataFrame([1,2,3,4]) >>> no6ch 0 0 1 1 2 2 3 3 4 >>> con=pd.concat([no4ch, no6ch], axis=0) >>> con 0 0 0 1 1 2 2 3 3 0 1 1 2 2 3 3 4 >>> con=pd.concat([no4ch, no6ch], axis=1) >>> con 0 0 0 0 1 1 1 2 2 2 3 3 3 4 >>> type(con) <class 'pandas.core.frame.DataFrame'>
pd.concat
の第一引数につなげたいデータをリストで渡す.また,
axis=0
:縦に並べてつなげるaxis=1
:横に並べてつなげる
となる.行数が一致していない場合は,
FutureWarning: Sorting because non-concatenation axis is not aligned. A future version of pandas will change to not sort by default. To accept the future behavior, pass 'sort=False'. To retain the current behavior and silence the warning, pass 'sort=True'.
と警告が出るが,互いにない要素をNaN
で埋めて結合してくれているので,特に問題はないかも.
また,con.fillna(0,inplace=True)
とすると,NaN
を全て0
に置き換えるといった処理ができる.
csv出力
以前自身がやってたやり方
# 書き込むファイルのパスが”path”に代入されているという前提のもとのスクリプト import csv data = list(data) with open(path,mode='w') as f: writer=csv.writer(f, lineterminator='\n') # writerオブジェクトを作成 try: writer.writerow(data) # 内容を書き込む except UnicodeEncodeError as ue: message(ue,'csvファイルの書き込みに失敗しました')
pandasでのやり方
# 書き込むファイルのパスが”path”に代入されているという前提のもとのスクリプト
data.to_csv(path)
その他引数を指定できる.
列数が一定でないcsvの読み込み
列(カラム)数が一定でないcsvファイルをpandasで読み込もうとすると,pandas.errors.ParserError
というエラーが出る.なので,
# 読み込むファイルのパスが”csv”に代入されているという前提のもとのスクリプト def csv_column_name(csv): column_cnt = 0 with open(csv,mode='r',encoding='utf-8') as f: for line in f: cnt = len(line.split(',')) if column_cnt < cnt: column_cnt = cnt return [x for x in range(column_cnt)] data = pd.read_csv(csv, engine='python',encoding='utf-8',header=None,names=csv_column_name(csv))
として,カラム名を作成して指定するとよい.
参考
- Pandasのデータを格納するオブジェクトDataFrameを理解する - DeepAge
- pandasでcsv/tsvファイル読み込み(read_csv, read_table) | note.nkmk.me
- pandasでcsvファイルの書き出し・追記(to_csv) | note.nkmk.me
- pandasで欠損値NaNを除外(削除)・置換(穴埋め)・抽出 | note.nkmk.me
- データ分析で頻出のPandas基本操作 - Qiita
- pandasでカラムサイズが一定でないcsv/tsvを読み込む : mwSoft blog
- Python §35 : pandas検討用のデータ確認 | hitochan007のブログ(Pythonの勉強ブログ)
- <Python, pandas> 列が一定でないデータを読み込む時、、 - ねこゆきのメモ
リスト内包表記 - [Python] for文処理が1行で書ける!素敵なリスト内包表記 - YoheiM .NET
- pandas.DataFrameのforループ処理(イテレーション) | note.nkmk.me
- Pythonデータサイエンスハンドブック(オライリージャパン)
- Pandasで合計を求めるsum関数の使い方 - DeepAge
- Pandasでforループを回して処理する方法と注意点 - DeepAge
- 【Python】スクレイピングしたcsvデータをHTMLに変換する | yamagablog
- pandas.DataFrame.to_html — pandas 1.1.1 documentation
- pandasのplotメソッドでグラフを作成しデータを可視化 | note.nkmk.me
- Matplotlib-ヒストグラムの徹底解説!(目盛り・階級幅/数・保存・正規分布・色など) - AI-interのPython3入門
- Matplotlibで簡単に日本語を表示する方法(Windows) | ガンマソフト株式会社
- python matplotlib 内で日本語を利用する(使用できるフォントを探す) - めも
- matplotlibのデフォルトのフォントを変更する | 分析ノート
- Matplotlib::設定
- pandas + matplotlib による多彩なデータプロッティング - Qiita
- Matplotlib で図のサイズを変更する方法 | Delft スタック
- 【Python】 matplotlib x軸を縦書きに|Hatimitu|note
- Matplotlib - matplotlib - 省略されてしまうラベルを表示したい|teratail
- pandasの使い方 〜列名や行名を取得する〜 - Pythonの勉強
追加でメモ(2020/9/2)
参考リストは上記の参考に追加してあります.
Dataframeのsum()
はSeriesなのでfor文でそのまま回せる
>>> a=pd.DataFrame({"a":{"c":1},"b":{"d":2}}) >>> a a b c 1.0 NaN d NaN 2.0 >>> a.fillna(0) a b c 1.0 0.0 d 0.0 2.0 >>> a a b c 1.0 NaN d NaN 2.0 >>> a.sum() a 1.0 b 2.0 dtype: float64 >>> type(a.sum()) <class 'pandas.core.series.Series'> >>> a.sum(axis=1) c 1.0 d 2.0 dtype: float64 >>> type(a.sum(axis=1)) <class 'pandas.core.series.Series'> >>> for i in a.sum(): ... print(i) ... 1.0 2.0 >>> a["a"] c 1.0 d NaN Name: a, dtype: float64 >>> a["a"] = a["a"]/a.sum()["a"] >>> a a b c 1.0 NaN d NaN 2.0 >>> a["a"] = a["a"]*2 >>> a a b c 2.0 NaN d NaN 2.0 >>> a = a.fillna(1) >>> a["a"] = a["a"]*2 >>> a a b c 4.0 1.0 d 2.0 2.0
fillna(指定の数もしくは文字列)
でNaNをすべて引数で置き換える.
また,method="ffill"
でNaNの前の数値で置き換えということができるみたい.
sum()
で各列もしくはaxis=1
で各行ごとの合計がSeriesとして得られるため,
Dataframeの場合for column_name, item in df.iteritems():
せずにfor文でそのまま回して値だけを得ることができる.
また,Dataframeの列を数値で四則演算すると,各要素ごとにその演算が適用される. 上記の場合0を割ってしまっている部分があり,そこはNaN値になっている.
>>> for i in a.sum().index: ... print(i,a.sum()[i]) ... a 6.0 b 3.0 >>> for k,v in enumerate(a.sum()): ... print(k,v) ... 0 6.0 1 3.0 >>> a.sum()[0] 6.0
Seriesは.index
でインデックスが得られるので,sum()
を呼び出すのが2度手間になってるが,
インデックスを得つつ値も同時に処理できるようになる.
リストなどでお馴染みのenumerate()
を使うとリストのようにインデックス番号と値が得られるが,
インデックスが文字列の場合は適さないかもしれない.
インデックス番号でもSeriesから要素の値を得られるのでどっちでも良いのかもしれないが...
要素へのアクセス
スライスや要素のアクセスについてちょっと分からなくなった... この記事内にスライスについて書いてはあるが,改めて復習させてください~
>>> a a b c 4.0 1.0 d 2.0 2.0 >>> a["a"] c 4.0 d 2.0 Name: a, dtype: float64 >>> a["b"] c 1.0 d 2.0 Name: b, dtype: float64 >>> a[:]["a"] c 4.0 d 2.0 Name: a, dtype: float64 >>> a["c"] Traceback (most recent call last):KeyError: 'c' >>> a[:"c"] a b c 4.0 1.0 >>> a[0:1] a b c 4.0 1.0 >>> a[:"c"]["a"] c 4.0 Name: a, dtype: float64 >>> a["a"][:"c"] c 4.0 Name: a, dtype: float64 >>> a["c":"d"] a b c 4.0 1.0 d 2.0 2.0 >>> a[:"d"] a b c 4.0 1.0 d 2.0 2.0 >>> a["d":"d"] a b d 2.0 2.0
Dataframeにおいて辞書型のように列インデックスもしくはカラム名でアクセスできるが,
行の場合はそうはいかないっぽい.
スライスならばインデックス名を使えるが,辞書型のように単独のインデックス名を指定して行を切り出すことはできないっぽい.
行の場合はa["d":"d"]
というように同じインデックス名を指定したスライスで指定の一行を得るといったトコロか...
要素の抽出・指定に関してはloc
・iloc
・ix
というメソッドでもできる.
とりあえずここでは辞書型的なアクセスについてまとめた.
CSVだけでなくHTMLテーブル出力もできる!
>>> a = pd.DataFrame({"":{"a":1,"b":2}}) >>> with open("test.html",mode="w",encoding="utf-8") as f: ... f.write(a.to_html()) ... 271
を実行すると以下の内容のtest.htmlというファイルが指定のディレクトリ下に作成される.
<table border="1" class="dataframe"> <thead> <tr style="text-align: right;"> <th></th> <th></th> </tr> </thead> <tbody> <tr> <th>a</th> <td>1</td> </tr> <tr> <th>b</th> <td>2</td> </tr> </tbody> </table>
a | 1 |
---|---|
b | 2 |
っとこんな感じの表ができ,マークダウンにコピペして埋め込められるから便利~
ただ,SeriesでやろうとするとAttributeError: 'Series' object has no attribute 'to_html'
エラーが出るのでSeriesにはこのメソッドがなく,DataFrameでしか使えないみたいなんですよね.
なのでSeriesをどうしてもHTMLテーブル出力したい場合は以上の例のように,
空のカラム名(ヘッダー)を作成してDataFrameにした上でto_html()
を使うといったトコロでしょうねぇ.
また,to_html()
はHTMLテーブル変換した文字列を作成するだけ(文字列を返却するメソッド)なので,
ファイル出力するにはwith
構文ファイルを作成して書き込まなければいけません.
to_csv
のように出力パスを引数に渡してメソッド単体で出力までできないです.
DataFrameからグラフプロット
最初にやっておくこと
プロットして出力・保存するにはmatplotlib
の力が必要.
以下をスクリプトの最初に書いておくと良いと思われる.
そして,PandasのDataFrameで日本語を扱っている場合デフォルトのフォントだと文字化けするので以下のように変更する必要がある.
import matplotlib.pyplot as plt from matplotlib.font_manager import FontProperties fp = FontProperties(fname=r'C:\WINDOWS\Fonts\msgothic.ttc',size=16) plt.rcParams["font.family"] = 'Yu Gothic' plt.rcParams["figure.figsize"] = (15, 15)
上記のfp = FontProperties(fname=r'C:\WINDOWS\Fonts\msgothic.ttc',size=16)
はプロットをmatplotlib
のメソッドを使って行う場合,
例えば日本語記述の凡例を示す場合に
plt.legend(loc="upper right", fontsize=10,prop=fp)
というように引数として使用するが,plt.rcParams
でデフォルトフォントを変更すれば必要ないかも.
適宜目的に応じて書いたり書かなかったりすればいい部分だと思われる.
日本語文字化け回避で重要なのはplt.rcParams
で日本語を扱えるフォントをデフォルトに変更しておくこと.
自身の環境下のmatplotlib
で扱えるフォントは
>>> import matplotlib.font_manager as fm >>> fonts = fm.findSystemFonts() >>> print([[str(font), fm.FontProperties(fname=font).get_name()] for font in fonts])
を実行すると示されるので,日本語が扱えそうなフォントの名前をコピペしてplt.rcParams
に指定すればおk.
ここではJupyter Notebookのハナシは省略.
plt.rcParams["figure.figsize"] は描画される図のサイズの変更. 最初の第一引数が横の長さで,第二引数が縦の長さの指定になる. あんまりに小さいと目盛りとか省略されるのでデータが大きいほど大きくしといたほうが良いと思われる.
DataFrameをプロット
以下data
にはDataFrameが代入されていると仮定してスクリプトを見てください.
plt.figure() data.plot() plt.grid(True) plt.xticks(ticks=[i for i in range(len(data.index))],labels=data.index,rotation=90) plt.savefig(title+".png") plt.close('all')
以上で折れ線グラフがPNGファイルとして出力される.
デフォルトでは折れ線グラフなので,
data.plot(kind="bar")
というように引数kind
を指定すると出力されるグラフの形状が変わる.
ちなみにbar
で棒グラフ.
まずplt.figure()
で描画領域の確保?だっけかな.
これは最初に一回やっておけばいい.
んで,次にPandas
のDataFrame側のメソッドを使ってグラフを描画.
plt.grid(True)
でグラフに格子が表示されるようになる.
いらない場合は記述しなくてよい.
plt.xticks
はX軸に関してグラフが描画された後に変更するもので,
ticks
は指定したリストの数だけ目盛りを表示し,
labels
がその目盛りに対して指定したリストを配置してラベル表示させ,
rotation
で目盛りに示されたラベル(文字)を指定した分だけ回転させる,といった具合.
実際やってみると分かるかと思う.
plt.xticks(ticks=[i for i in range(len(data.index))],labels=data.index,rotation=90)
を記述しておくことで
通常省略されるところをインデックス全て表示されるようになる.
ただし,インデックスが多いほど見にくくなったり,隣と重なって文字がつぶれてしまうこともあるので
状況に応じて使おう.
plt.savefig
で画像として出力している.plt.show()
でスクリプト内で表示だったかな.
plt.close('all')
で描画のリセット.スクリプト内で複数グラフを描画する場合は,
次のグラフを描画する前にやっておく必要がある.
でないと前のグラフの描画が残ったままでグラフが重なってしまう可能性がある.