機械設計業務(構造系)の技術者+金融のpandasチートシートです
追加でインストール
- dataframeをマークダウン形式の表で出力する
pip install pytablewriter
import pytablewriter
writer = pytablewriter.MarkdownTableWriter()
writer.from_dataframe(df)
writer.write_table()
サンプルの作成
- testデータを作る
import pandas as pd
df = pd.DataFrame([1,2,3,4,5],columns=["対象列"])
df = pd.DataFrame([[1,1],[2,2],[3,3],[4,4],[5,5]],columns=["対象列1","対象列2"])
# 上と同じ
df = pd.DataFrame({"対象列1":[1,2,3,4,5],
"対象列2":[1,2,3,4,5]
})
対象列1 | 対象列2 |
---|---|
1 | 1 |
2 | 2 |
3 | 3 |
4 | 4 |
5 | 5 |
- 時系列のサンプル
import datetime
i=1000
now=datetime.datetime.now()
li=[now,i,i+10,i-10,i+5]
df = pd.DataFrame([li])
for p in range(3000):
i=i+10
if p != 2:
li=[now+datetime.timedelta(seconds=p*45),i,i+10,i-10,i+5]
else:
li=[now+datetime.timedelta(seconds=p*45)," ","","",""]
# df.replace(' ', '')
df=df.append([li], ignore_index=True)
df.columns=["time", "open","high","low","close"]
df.to_csv("test.csv", index=False)
サマリを表示
df_t.describe()
最頻を取得(多数決で決めたい場合に使った)
df_temp.mode().iloc[[0]]
テーブルの表示設定
pd.options.display.max_columns = None
pd.options.display.max_rows = 100
pandas tableを綺麗に表示
from plotly.offline import init_notebook_mode, iplot
import plotly.figure_factory as ff
iplot(ff.create_table(df))
テーブル間のdiff,差分
データを変更した際に、変更箇所のみを抽出したい時に使う
【use case】:レッドマインのチケットをcsvで纏めて変更した際に、サーバへ反映するチケットidを抽出する
import pandas as pd
df = pd.DataFrame([["リンゴ",1],["オレンジ",2],["いちご",3],["レモン",4],["マンゴー",5]],columns=["id","数量"])
df_edit = df.copy()
df_edit.loc[df_edit['id']=="レモン", '数量']=15
df_diff = pd.concat([df,df_edit])
df_diff = df_diff.drop_duplicates(keep=False)
# keep="last"でdf_edit側の値を残す
df_diff.drop_duplicates(subset="id",keep="last")
よく使うグラフ
- 散布図
'''marker https://matplotlib.org/api/markers_api.html
.:point ,o:circle, v:下三角, ^:上三角, s:四角 +:plus
linestyle https://matplotlib.org/gallery/lines_bars_and_markers/linestyles.html?highlight=linestyle
solid,dotted,dashed,dashdot
color https://matplotlib.org/examples/color/named_colors.html
b:青 (Blue) g:緑 (Green) r:赤 (Red) c:シアン (Cyan) m:マゼンタ (Magenta) y:黄 (Yellow) k:黒 (Black) w:白 (White)
'''
import matplotlib.pyplot as plt
import seaborn as sns
sns.set()# SeabornのデフォルトStyleを使用
fig = plt.figure(figsize=(8,4))# グラフのサイズを設定(横×縦)
ax = fig.add_subplot(111)
df.plot(x='x', y='z', ax=ax,
linestyle='dashed', #線種
marker='o', #マーカー
color='darkgreen', #色
linewidth = 0.5) #線の幅
df.plot(x='x', y='y', ax=ax,
linestyle='dashed', #線種
marker='o', #マーカー
color='darkblue', #色
linewidth = 0.5) #線の幅
ax.set_title("TEST")# TITLEを設定
ax.set_xlim(0,2*np.pi)# X軸の範囲
ax.set_ylim(-1,1)# Y軸の範囲
ax.set_xticks([0, np.pi, np.pi*2])# X軸のTick(目盛)の位置を設定
ax.set_xticklabels([0, 'π', '2π'])# X軸のTick(目盛)の表記を設定
ax.set_yticks([-1, -0.5, 0, 0.5, 1])# Y軸のTick(目盛)の位置を設定
ax.set_xlabel('X [RAD]')# X軸のラベルを設定
ax.set_ylabel('X [RAD]')# Y軸のラベルを設定
plt.show()# グラフ表示
- 動くグラフ
%matplotlib inline
import pandas as pd
import cufflinks as cf
cf.set_config_file(offline=True, theme="white", offline_show_link=False)
cf.go_offline()
"""
pandas plot 動くグラフ 図 可視化 https://www.sejuku.net/blog/61788
https://qiita.com/inoory/items/7c8ca9fd5e1aca3e2e72
"""
df.iplot(xTitle="X軸名", yTitle="Y軸名", title="タイトル")
df.iplot(kind="scatter" ,mode='markers', x="col1", y=["col2"]) # 散布図
df.iplot(kind="scatter" ,mode='lines+markers', x="col1", y=["col2"]) # 散布図 線つき
df.iplot(kind="scatter" ,mode='lines', x="col1", y=["col2"]) # 散布図 線のみ
df.iplot(subplots=True, shape=(2,1), shared_xaxes=True)#subplot
"""
pandas plot 動くグラフ 図 可視化
figの中身はdict型
"""
fig = df.figure(secondary_y="col2", yTitle="ylabel", xTitle="xlabel")
fig.layout.yaxis2.title = "y2label"
df.iplot(fig)
- 二軸のグラフ
$を使うと、latexが使える
import matplotlib.pyplot as plt
import numpy as np
import pandas as pd
%matplotlib inline
# MS Gothic,MS Mincho,,Yu Mincho, Malgun Gothic
plt.rcParams['font.family'] = 'MS Gothic' #全体のフォントを設定(日本語が使えるようにする)
plt.style.use('dark_background') # テーマを設定
plt.rcParams["font.size"] = 15 #フォントサイズの設定
df = pd.DataFrame({"time(s)":[0,100,200,300,1000],
"Heat transfer coefficient (W/m^2)":[30,90,45,50,20],
"temparature (K)":[500,600,650,450,600]
})
#下記を調整して、第二軸がいい感じになる所を見つける
min_y1 = 0 #第一軸の最小値
max_y1 = 100 #第一軸の最大値
sep = 11 #分割
x = df.columns[0]
y1 = df.columns[1]
y2 = df.columns[2]
y1_label = y1.replace(" ",r"\ ")
y2_label = y2.replace(" ",r"\ ")
x_label = x.replace(" ",r"\ ")
fig = plt.figure(figsize=(10,8))
ax1 = fig.add_subplot(111)
ln1=ax1.plot(df[x], df[y1],'C0',label='$'+y1_label+'$')
ax2 = ax1.twinx()
ln2=ax2.plot(df[x],df[y2],'C1',label='$'+y2_label+'$')
h1, l1 = ax1.get_legend_handles_labels()
h2, l2 = ax2.get_legend_handles_labels()
ax1.legend(h1+h2, l1+l2, loc='upper right')
ax1.set_title("入熱条件")
ax1.set_xlabel('$'+x_label+'$')
ax1.set_ylabel('$'+y1_label+'$')
ax1.grid(True)
ax2.set_ylabel('$'+y2_label+'$')
ax1.set_yticks(np.linspace(min_y1, max_y1, sep))
ax2.set_yticks(np.linspace(ax2.get_yticks()[0], ax2.get_yticks()[-1], len(ax1.get_yticks())))
数値以外の文字列を含む場合の処理
参考:pandas.DataFrame で数値以外の要素の抽出
# 数値ではない型の要素の抽出
pic = df[['price']][df['price'].apply(lambda s:pd.to_numeric(s, errors='coerce')).isnull()]
# ',' の削除,'/' が含まれる要素を欠損値で置換
change_data = pic['price'].str.replace(',','').mask(pic['price'].str.contains('/'), np.nan)
# 元データのコピーを作成し,該当箇所を置換
df_c = df.copy()
df.loc[pic.index,'price'] = change_data
#'price' 列の数値を数値型に変換し,欠損値を含む行を削除
df_c['price'] = pd.to_numeric(df_c['price'], errors = 'ignore')
df_out = df_c.dropna()
既存の列を使って処理して、新しい列を作成する
- 同一行内の計算
dataseriesにして計算すれば、エクセルと同じイメージで計算できる
ずらしたい場合は、ずらしたdataseriesを作成して計算すればよい
df["計算"] = df["対象列1"] + df["対象列2"]
対象列1 | 対象列2 | 計算 |
---|---|---|
1 | 1 | 2 |
2 | 2 | 4 |
3 | 3 | 6 |
4 | 4 | 8 |
5 | 5 | 10 |
- 累積和 cumsum
イメージ:excelで積分。材料力学の梁の計算で、荷重からSFDを求める際など
df["累積和"] = df["対象列"].cumsum()
対象列 | 累積和 |
---|---|
1 | 1 |
2 | 3 |
3 | 6 |
4 | 10 |
5 | 15 |
- 差分 diff
イメージ:excelで微分。材料力学の梁の計算で、SFDから荷重を求める際など
オプションで範囲を指定することができる
df["差分"] = df["対象列"].diff()
対象列 | 差分 |
---|---|
1 | NaN |
2 | 1 |
3 | 1 |
4 | 1 |
5 | 1 |
- 最大値 max
df["max"] = df.max(axis=1)
対象列1 | 対象列2 | max |
---|---|---|
1 | 1 | 1 |
2 | 2 | 2 |
3 | 6 | 6 |
4 | 8 | 8 |
10 | 5 | 10 |
- 1行ずらした列を挿入
shiftの引数で、ずらす数をコントロールできる。
https://note.nkmk.me/python-pandas-shift/
df["yesterday_close_value"] = df['close'].shift()#最後の行は自動で削除してくれるので、tableの数は一致
close | yesterday_close_value |
---|---|
1 | NaN |
2 | 1 |
3 | 2 |
4 | 3 |
5 | 4 |
- 関数を使用する
イメージ:excelで各セルを使って関数を適用。
df["関数"]= df.apply(lambda d: d['対象列1'] + d['対象列2'], axis=1)
#上と同じ
def func(df):
d = df['対象列1'] + df['対象列2']
return d
df["関数"] = df.apply(func, axis=1)
対象列1 | 対象列2 | 関数 |
---|---|---|
1 | 1 | 2 |
2 | 2 | 4 |
3 | 3 | 6 |
4 | 4 | 8 |
5 | 5 | 10 |
欠損値nanの扱い
df.dropna() #nanの行を削除(行内に一つでもnanがあれば 行を削除)
df.dropna(axis=1) #nanの列を削除(列内に一つでもnanがあれば 列を削除)
df.dropna(how='all') #すべての値が欠損値である行を削除する
df.dropna(how='all', axis=1) #すべての値が欠損値である列を削除する
df.dropna(how='all').dropna(how='all', axis=1) #行列両方に適用
df.dropna(subset=['age', 'state']) # 特定の列に欠損値がある行を削除する
df.dropna(subset=[0, 4], axis=1) #subsetで指定した行に欠損値がある列を削除
df.fillna(0)#引数に置き換えたい値を指定するとすべての欠損値NaNがその値で置き換わる
df.fillna({'name': 'XXX', 'age': 20, 'point': 0})#引数に辞書を指定すると、列ごとに異なる値が代入
df.fillna(method='ffill') #前の値で置き換え
df.fillna(method='bfill') #後ろの値で置き換え
df.apply(pd.Series.interpolate)#nanを前後の線形の値で埋めたい場合 https://openbook4.me/projects/183/sections/777
文字列の置換
df = df.replace([' ', ' : ',"nan"," : : "], [np.nan, np.nan, np.nan, np.nan])#特定の文字列をnanに変更
列への処理
- 列volumeの名前をVolumeに変更 inplace=Trueで上書き
df.rename(columns = {'volume':'Volume'}, inplace=True)
- 列番号を列名から取得する
df.columns.get_loc('volume')
- 条件を付けて行の削除
df.drop(df.query('age < 25').index)
- 列の削除
df=df.drop("high", axis=1)#列の削除
- 列の並べ替え
df=df.loc[:,["x","y","z"]]#列
の順番を変更["y","x","Z"]を["x","y","z"]に並び替える
型
- 型を確認
df.dtypes # 型確認
型の種類
'b' boolean
'i' (signed) integer
'u' unsigned integer
'f' floating-point
'c' complex-floating point
'O' (Python) objects
'S', 'a' (byte-)string
'U' Unicode
'V' raw data (void)型変更
df=df.apply(pd.to_numeric, errors='ignore') #型変更(数字に変換)エラーは無視
df['現在値'].apply(pd.to_numeric, errors='coerce') #型変更(数字に変換)エラーはnanとなる
df['i'].astype(str) #数値を文字列に変換
df['i'].astype(int) #整数intに変換
df['i'].astype(float) #浮動小数点floatに変換
条件を与えて、データを抽出する
df[df['age'] < 25] #比較演算子で条件指定
df[df.a > 0] # aというカラムが0より大きいものを抽出
df[~df.a > 0] # aというカラムが0より大きいもの"以外"を抽出
df[df['state'].isin(['NY', 'TX'])]#in演算子で条件指定
df[(df['age'] < 25) & (df['point'] > 65)]#複数条件を指定
df_para.columns[df_para.columns.str.contains("MACD")]#部分一致
#query
df.query('age < 25')#条件を文字列で指定
df.query('not age < 25') #否定はnot
df.query('24 <= age < 50') #条件指定のように範囲を指定可能
df.query('age < point / 3')#列と列との比較や、算術演算子で計算して比較
df.query('state == "CA"') #一致、==
df.query('state != "CA"')#不一致は!=
df.query('state in ["NY", "TX"]')
df.query('name.str.endswith("e")', engine='python')# 特定の文字列で終わる
df.query('name.str.contains("li")', engine='python')#特定の文字列を含む
df.query('name.str.match(".*i.*e")', engine='python')#正規表現のパターンに一致する
df.query('index % 2 == 0')#index列に対する条件
val = 80 #変数を使う
df.query('point > @val')
df.query('age < 25 and point > 65')#&でもOK
df.query('age < 25 or point > 65')#|でもOK
df.query('age_year > 25', inplace=True)#引数inplaceで元のオブジェクトを更新
df.query('not age < 25 and not point > 65')#否定はnot
3つ以上での条件も同様だが、andのほうがorより優先順位が高いなど順番によって結果が異なるので、先に処理したいまとまりを括弧で囲んだほうが無難。
df.query('(age == 24 | point > 80) & state == "CA"')
pandas データフレームの中からリストにある列の選択
li = ['現在値','出来高',.....]
df1=df[li]
重複の確認と処理
df.duplicated().any() #重複チェック
df.duplicated(['x', 'y']).any() #部分的な重複チェック x と y 列の重複チェックは True
df.drop_duplicates(['x', 'y']) # 重複データを削除 前のデータを残す
df.drop_duplicates(['x', 'y'], keep='last') #重複データを削除 後のデータを残す
イタレーション(おすすめしない)
- 縦方向にループ
for i, v in df.iterrows(): #下記の場合は、X,Yの列名があり、行方向へのループ(列の場合は行名を入れる)
print (i, v['X'], v['Y']) # iは行または列名 v は Series
- 横方向にループ
for i, v in df.iteritems():
print (i, v['a'], v['b'], v['c']) # v は Series
上下反転(逆順)
df.iloc[::-1]
列の順序を反転
df[df.columns[::-1]]
セルの選択
df.loc[df['項目']=="レモン", '数量'] #項目列レモンの数量列を選択
df.iloc[0,0] #行列を数字で選択
df.iat[0,0] #行列を数字で選択 こちらの方が早い
df.iat[0, df.columns.get_loc('volume')] #行列を数字で選択
df.loc['Bob', 'age'] #行列を名前で選択
df.at['Bob', 'age'] #行列を名前で選択
書き込み
- csv
df.to_csv("test.csv", index=True)
df.to_csv("test.csv", index=False)
読み込み
- csv
df = pd.read_csv(os.path.join(folder_path,csv_list[0]), index_col=0)#一番左の列をindexにする
df = df.astype("float")
df.index=pd.DatetimeIndex(df.index)
文字列 すべてのセルの文字数を8個にする
moji = lambda x: x + " "* (8-len(x))
a=moji(str(253)) #253 -> 253
print(a,len(a)) # 253 8
df= df.applymap(moji) #すべてのセルの文字数を8個にする
"""
pandas 文字列 各列の文字列を結合 https://qiita.com/piroyoung/items/dd209801ca60a0b00c11
"""
df=df.assign(text_output=lambda df: df.apply(lambda row: "".join(row), axis=1))
indexの指定
df=df.set_index("時間")
df.index=pd.DatetimeIndex(df.index)
時系列
today = pd.Timestamp(date.today())
pd.Timestamp(2019,12,15,9,10,1) #2019年12月15日9時10分1秒
pandas 読み込み
- 時系列データ
df=pd.read_csv("test_time.csv", header=0, index_col='time', parse_dates=True,na_values=[" ", 0],dtype="float")#読み込み
df.index=pd.DatetimeIndex(df.index)#インデックスを時間に指定
df=df.between_time('9:00', '11:30')#特定の時間のみを取り出す(日時は関係なし)
df_temp = df[df.index >= pd.Timestamp(2019,1,25)]#2019年1月25日以降のデータを出す
- codec error 読み込み時にcodecのエラーが出る場合
import codec
with codecs.open("file.csv", "r", "Shift-JIS", "ignore") as file:
df = pd.read_table(file, delimiter=",")
- 数値で読み込み(タイプを指定)
df=pd.read_csv( "test.csv", header=0, dtype="int")
df=pd.read_csv( "test.csv", header=0, dtype="float")
- 文字列で読み込み(タイプを指定)
df=pd.read_csv( "test_other.csv", header=0, dtype="str")#文字列
- webから読み込み
url = 'http://www.jma.go.jp/jp/warn/329_table.html'
fetched_dataframes = pd.io.html.read_html(url)
sqlite
sqliteは同時書き込みは苦手。書き込みは1個のみがよい。
- 読み書き共通
import pandas as pd
import sqlite3
db_name = os.path.join(os.environ['userprofile'],"temp","test.db")
conn = sqlite3.connect(db_name)
#--------------
# 読み書き処理
#--------------
conn.close()
- 書き込み
testテーブルに対して、index込で書き込みを実施する
df_temp.to_sql('test',conn,if_exists='append',index=True)
- 読み込み
testテーブルからデータを読み込み
最新5000個のデータを取得する
order by jikan desc limit 5000 : 時間列を降順でソートして、5000個を取得
df_read_test=pd.read_sql_query('SELECT * FROM {} order by jikan desc limit 5000'.format('test'), conn,index_col="jikan")
df_read_test.index=pd.DatetimeIndex(df_read_test.index)
開始行と取得数を指定してデータを取得する
limit 100,1000 : 100を開始行として、1000個を取得 100から1100行までを取得する
number_of_data=str(self.start_number) +"," +str(self.inkuri)
sql_bun = 'select * from {} '+'limit '+str(number_of_data)
df=pd.read_sql_query(sql_bun.format('stock_data'), conn, index_col='時間')
df.index=pd.DatetimeIndex(df.index)
df = df.replace([' ', ' : ',"nan"," : : "], [np.nan, np.nan, np.nan, np.nan])