Numpy & Pandas
まじで毎回忘れる
統計量出す
df.describe()
df.describe(exclude='number')
でカテゴリデータ
Series も describe ある & datetime なら first / last も出る
思考停止で使える
カラム名や型など情報見る
df.info()
欠損値探す
df.isnull().sum()
df.info()
でもだいたい出る
df[df.isnull().any(axis=1)]
1つでも欠損値がある行を取り出す
df[~df.isnull().any(axis=1)]
1つでも欠損値を含む行を除く
最初と最後の5行出す
head, tail 使う他に print(df, 5)
もある
ある列の最大値を持つ行
df.loc[[df['col'].idxmax()]]
idxmax で行番号を得て loc で取得
テーブルの加工
df['col'] -> Series
特定の名前の列をとりだす
df[['col1', 'col2']] -> DataFrame
列選択して DataFrame に(1列でもよい)
df.drop(['col1', 'col2'], axis=1) -> DataFrame
列指定して捨てる
df.drop(df.tail(1).index, inplace=True)
末尾1行捨てる、合計とかが入ってて邪魔な時に
a.columns = ['_'.join(filter(None, map(str, col))).strip() if len(col) > 1 else str(col[0]) for col in a.columns.values]
マルチレベルの列が混ざっている場合に、 _
で結合しつつフラットにする
groupby して agg で1つのカラムの min と max を出したりすると起きる
df.melt(id_vars, value_vars, var_name, value_name)
id_vars
を列として維持し
value_vars
列を var_name
列として縦持ちにする
値を value_name
列にする
毎回どうだっけ? てなるので絵にした
melt よりイメージはつきやすい
df.pivot(index, columns, values)
上の絵の逆の操作をやるのが
df.pivot(index=['ID', 'Region'], columns='Quarter', values='Sales')
DataFrame
複製
df.copy()
条件に合う行を取り出す
df[df['column'] > 1000]
df.query('column > 1000')
遅いのでチェーンで書きたいときだけ
Bool で比較するときは is
ではなく ==
df[df['label'].isin(['hoge', 'fuga'])]
isin
で特定の値のものにしぼる
否定 IS NOT IN
は ~Series
になる
df[~df['label'].isin(['hoge', 'fuga'])]
df[0:1]
などのスライスで行番号で参照
df[df['name'].str.contains('hoge')]
文字列として含む
contains('hoge', na=False)
で空なら除ける
datetime 文字列比較でなんかいける
df[df['timestamp'] >= '2022-08-01']
datetime と比較してもいい
df[df[date] > datetime(2023, 6, 29)]
カッコでくくって &
や |
で複数条件
df[(df['name'] == 'pokutuna') | (df['name'] == 'oneetyan)]
列を取り出す
df['column_name'] -> Series
df.pop('col')
で削除しつつ取り出す、目的変数分ける時などに
並び替え
df.sort_values('colmun', ascending=False)
ascending=False
で降順
横に結合
df['append_column'] = series
index フリ直し
sort したあとなど index がバラバラになる
df.reset_index(drop=True)
で index を振り直し元の index を消す
json のパース
pd.json_normalize()
を使う
入力は json じゃなくて dict でよい
data には list か pd.Series を期待している
BigQuery & Cloud Logging などで jsonPayload
を展開して元の DataFrame にくっつけたい
normalize_concat.pypayload = pd.json_normalize(df['jsonPayload']).add_prefix('jsonPayload.')
df = pd.concat([df, payload], axis='columns')`
record_prefix='jsonPayload.'
してもうまくいかない
タイムゾーン
変換するなら tz_convert
セットするなら tz_localize
?
時刻のパース
df['parsed'] = pd.to_datetime(df['year'], format="%Y")
df['dt'] = pd.to_datetime(df['timestamp']).dt.tz_convert('Asia/Tokyo')
format
20230614
みたいなパースは %Y%m%d
23/01/02
は %y/%m/%d
pd.load_csv
や pd.load_table
で読む
table のほうは区切り文字が \t
列名、 header=None
や names=('A', 'B')
BigQuery から timestamp & jsonPayload を SELECT してきてパース
format.pypayload = pd.json_normalize(df['jsonPayload']).add_prefix('jsonPayload.')
df['dt'] = pd.to_datetime(df['timestamp']).dt.tz_convert('Asia/Tokyo')
df = pd.concat([df, payload], axis='columns')
df = df.drop(['timestamp', 'jsonPayload'], axis="columns")
df
複数の Series を prefix で選択
df.filter(regex='^prefix',axis=1)
で、 prefix
から始まる Series を集めた df が返る
datetime を index にする
df["date"] = pd.to_datetime(df["date"])
df.set_index("date", inplace=True)
df['2023']
, df['2022-01':'2022-12']
のような指定で部分を取り出せる
こういう問題もある
連番振る
df['num'] = range(0, len(df.index))
転置
df.T
各列の平均や標準偏差の Series を取る時などに
df.describe().T['mean']
Series
series.describe()
とりあえず
series.mean()
series.value_counts()
値ごとにカウントする, dropna=False
で NaN
をカウントできる
series.append(series)
別の series
をくっつける
s1.adppend(pd.Series([1,2,3], index=['foo', 'bar', 'baz']))
series.dtype
で型を見る
series.isin(['foo']).any()
series
のなかに 'foo'
を含むかどうか
isin
は Series の各値に対して含むかどうかチェックする、True/False の series ができるので any
でいずれかが True であることを調べる、 all
もある
series.str.split('_').str.get(0)
文字列として _
で切って 0
番目のものだけ
GroupBy
groupby
で返るのは DataFrameGroupBy
や SeriesGroupBy
オブジェクト
メソッドを呼ぶとグループごとに適用された結果が返る
df.groupby(['a', 'b'])
で複数のカラムでグループ化
df.groupby(..., as_index=False)
groupby
したカラムの値を index にしない
表を操作しているイメージや
Altair で使う時は index じゃないほうがいい
最後に reset_index()
してもよい
グループに対してチェーンして集計したい時はそうなる
df.groupby(...).first()
各グループの最初の値を返す
df.groupby(...).agg({'col1': np.sum, 'col2': ['sum', 'count'] })
group に対して agg
で集計を行う
関数名を渡せる
count
: 行のカウント
nunique
: ユニークカウント
lambda を渡せる
df.groupby(...).agg({'col1': lambda x: (x==True).sum()}
col1 が True のものをカウント
各グループのサイズを得るには .size()
df.groupby(...).size()
df.groupby(...).apply(...)
apply の lambda が何返すかによって結果全然異なる?
DataFrame / Series / スカラー
スカラーを Series にして名前を付けて返すと新しい列の名前を指定しつつ集計できる
yokuyaru.pydf.groupby([
pd.Grouper(key="timestamp", freq="D"),
"subgroup",
]).apply(lambda x: pd.Series(
{"new_col": x["a"].str().contains().count() /x["b"].count() }
)).reset_index()
グループ化してカウントして表にする
data.groupby(['HomePlanet','Destination','Solo','Cabin_deck'])['Cabin_deck'].size().unstack().fillna(0)
groupby に渡す
df.groupby(pd.Grouper(key="dt", freq="D"), as_index=False).size()
df.groupby(pd.Grouper(key="date", freq="D")).sum()
(時以降も入っている) datetime
のまま日単位でグループ化する
複数渡せる
df.groupby([pd.Grouper(key="dt", freq="D"), pd.Grouper(key="is_user")])
resample
でも似たようなことができる、grouper のほうが明確で好みかなあ
df.resample('30T', on='dt').sum()
単にユニークにしたい場合は drop_duplicates
Binning
cut, qcut を使う
bin 用のSeries を作る
df['bin'] = df[['rank']].apply(pd.cut, bins=list(range(0, 1100, 100)))
順位が入った Series の値を binning する
加工
map
df['mapped'] = df['col']
速度は map > apply >>> for ループ?
時刻のパース
df['dt'] = pd.to_datetime(df['timestamp'])
in-place に変換する方法はないのかな?
json のパース
pd.json_normalize(df['json'])
obj.field
というカラムになる
展開して横につなげるには
pd.concat([df, pd.json_normalize(df['json'])], axis='columns')
区切り文字を .
から変えたり、prefix を付けたり
pd.json_normalize(df['ua'], sep="_").add_prefix('ua_')
特定の区間の値に制限する
df.clip(None, 1.0, inplace=True)
繰り返す
np.tile([1,2,3], 3) #=> [1,2,3,1,2,3,1,2,3]
移動平均
df.rolling(7).mean()
週ごと・月ごと
df.resample("W").sum().plot()
M
ならmonth
30T
なら 30分
型の変換
たまに df.dtypes
、 df['col'].dtype
で確認する
あるあるかつハマるのでダルい
np.NaN
は float なので、 int64
の列に NaN 込みのデータを結合すると float64
になる
pd.Series([np.NaN]).dtype # => dtype('float64')
bool を 0, 1 に
convert_bool.pydef convert_bool(in_df):
df = in_df.copy()
for col in df.columns:
if df[col].dtype == 'bool':
df[col] = df[col].astype(int)
return df
実際には bool だけじゃなく True / False / NaN だったりする
seaborn の pairplot などで bool 避けたいだけなら 1, 0, -1 などに replace する
df[col].replace({True: 1, False: 0, np.nan: -1})
plot
styler
df.style.highlight_max
TODO style.pipe
でやる
npz
numpy バイナリフォーマット
with np.load(path) as data: ...
data.files
で列見れる
progress_apply
プログレスバーを表示しながら apply できる
tqdm.pyfrom tqdm import tqdm
tqdm.pandas()
np.ndarray の Series を np.ndarray にネストさせる
何言っているのか
既に個々のセルの値が np.array
なので、 to_numby
だと
np.array([np.array([...]), np.array([...], ...])
になる、 np.stack
するとよい
stack.pyseries = pd.Series([
np.array([1,2,3]),
np.array([4,5,6]),
np.array([7,8,9]),
])
# を
np.array([[1,2,3], [4,5,6], [7,8,9]])
# にしたい
np.array(series.tolist())
# もしくは
np.stack(series.to_numpy())