見出し画像

データ前処理①

データを前処理する手順のメモ。読み込み ~ 集計・可視化まで。


ライブラリ読み込み

import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
%matplotlib inline #JupyterLabでグラフ表示
import japanize_matplotlib #matplotlibグラフ見出し等日本語化
import seaborn as sns

データ読み込み、日付データ変換

※日付データに変換すると時系列で集計・グラフが作成可能。

#データ読込時に日付データ変換、インデックスへ
df = pd.read_csv('〇〇.csv',encoding = '〇',parse_dates=True,index_col='col')
#データ読込後に日付データ変換→インデックスにする
df['col'] = pd.to_datetime(df['col'])
df = df.set_index('col')

#ヘッダーがない csv読込。1番目と6番目の列にdt,vと名前指定
df2 = pd.read_csv('shpg_headerless.csv', header=None, usecols=[0, 5], names=['dt', 'v'])

#タブ区切りファイル読込
df3 = pd.read_csv('shpg.tsv', index_col=0, sep='\t')

#excelファイルから特定のシートを読込
df_data = pd.read_excel(io="pop202003.xls",sheet_name="市町村別人口")

データ確認

#各列のデータ型の参照
df.info()
#データの先頭数行を閲覧する
df.head()
#データの末尾数行を閲覧する
df.tail()
#データの次元数(何行、何列あるか)
df.shape
# 値の個数
df["column"].value_counts()
pd.value_counts(df["column"])
#ユニークな要素数の確認
df.nunique()
#最大値、最小値、平均値、標準偏差などの参照
df.describe()
#質的データに対する統計量
df.describe(include=['O'])
#count:データの個数,unique:ユニークな(一意な)値の個数,,top:最も出現頻度の高い値(最頻値)
#freq:最頻値の出現回数
#最頻値
print(df['age'].mode())
#データ間の相関
df.corr()

データ型変換・置換

#データ型の変換(新しい列の追加にて実現)
df["品番2"] = df["品番"].astype(object)
#データ型の変換(元の列にそのまま代入して変換)
df_s["test"] = df_s["test"].astype(object)
df_s["test"] = df_s["test"].astype(str) 
df_s["test"] = df_s["test"].astype(float)
df_s["test"] = df_s["test"].astype(int) 

# 型違いの値を強制的に排除(To Nan)しつつ数値型に変換
df_s["test"] = pd.to_numeric(df_s["test"] , errors="coerce")

#数字を丸める
df['fare'] = df['fare'].round()

#'sex'列のfemaleをPythonに置換
df['sex'] = df['sex'].replace('female','Python')

#文字列の一部を削除
import re
df['name'][0] = re.sub('Elisabeth','',df['name'][0])
#部分一致の文字列消去にはre.sub()を使用 re.sub('消したい文字列','','元々の文字列') のように使う
#完全一致で文字列を消去するときはreplaceを使用

#都道府県列と市区町村列を'_'で結合してtest2列に表示
df5['test2'] = df5['都道府県'].str.rstrip() +'_'+ df5['市区町村']
#右側の空白を削除 str.rstrip()
#両端の空白を削除 str.strip()
#左側の空白を削除 str.lstrip()

行・列の操作

#特定の条件でカラム抽出(loan_statusがAかBのカラムだけ抽出)
loans = loans[ (loans['loan_status'] == 'A') | (loans['loan_status'] == 'B') ]
#cから始まる列名検索
df_cereal[list(filter(lambda col: col.startswith('c'), df_cereal.columns))]
df_cereal.loc[:, df_cereal.columns.str.startswith('c')]
#dfのname列に文字列「Mrs」が含まれるデータを表示
#特定の文字列を含むデータを抽出したいときはstr.contains()を使用 engine='python'を指定しないとエラーが出る
df.query('name.str.contains("Mrs")',engine='python')
#列名の変更
df2 = df2.rename(columns={'English' : 'Biology','Mathematics' : 'Physics', 'History' : 'Chemistry'})

#列の削除
df = df.drop('body',axis=1)#行の削除はaxis=0 dropは元のdfを変更しないので上書きする
data = data.drop(columns=['id'])
ser_sugars = df_cereal_copy.pop('sugars')#pop()→列を削除すると同時に削除された列をSeriesオブジェクトに格納

#列の挿入(insert()の引数に列の位置番号、列名、値を順に指定)
df_cereal_copy.insert(1, 'rating_int',df_cereal.rating.astype(int))
#列の挿入(assign()関数にキーワード引数列名=値を指定 ※元のdfは変更されない)
df_cereal.assign(country='US')

#列の結合
df['test'] = df['cabin'] + '_' + df['embarked']
#数値型を文字型に変換して結合
df['test'] = df['age'].astype(str) + '_' + df['embarked']

#並び替え(Dateを昇順で並び替えて元のdf更新)
df.sort_values(by='Date', ascending=True, inplace=True)

#行をシャッフルして表示する
sample(frac=1)
#インデックスを振り直すとき
df.reset_index(drop=True)#並び替えられたインデックスを再設定→drop=True

#行と列を入れ替え
df2 = df2.transpose()
df2.T

テーブル結合

#df2とdf3をnameキーで左結合
df2 = pd.merge(df2,df3,on='name',how='left')
#左結合では、df2に存在するレコードにdf3のレコードを結合する
#内部結合(innner)では、df2とdf3の共通のキーのみで結合する
#外部結合(outer)では、df2とdf3の両方に存在するレコードが残るように結合する

#df2とdf4を水平方向で結合
df2 = pd.concat([df2,df4],axis=1)#垂直はaxis=0
#dfを3つ水平方向に結合してindexとPID列で昇順に並び替え
df_midwest = pd.concat([df_mw1.set_index('PID'), df_mw2.set_index('PID'), df_mw3.set_index('PID')], axis=1).reset_index()
#重複した列を消去
df2 = df2.loc[:,~df2.columns.duplicated()]

欠損値処理

# 欠損値のカウント
df.isna().sum() #欠損値じゃないカウント notnull().sum()
# 欠損値のカウント(特定の列)
df_s["不良内容.1"].isna().value_counts()
# 欠損値がある行を表示(特定の列に)
df_s.loc[df_s["不良内容.1"].isna()]

# dfに1個でも欠損値があったら行を削除する
df = df.dropna()
#全ての要素が欠損値だったら削除→how = 'any'
df.dropna(axis = 0, how = 'any', inplace=True)
#特定の列の欠損値があった行を削除
df = df.dropna(subset=['カラム1','カラム2'])
# 閾値を決めてドロップする(欠損した列を3列以上もつ行を削除する)
df.dropna(thresh=3,inplace=True)# inplaceしないとデータに反映されない。

# 欠損値を含まないデータの抽出
df_data = df_data[~df_data['2023-03-01_人口'].isna()]

#欠損値の穴埋め(平均値、最頻値で穴埋め)
df.loc[df["単価"].isna(),"単価"] = 0
df['age'] = df['age'].fillna(30)
df_copy['age'] = df_copy['age'].fillna(df_copy['age'].mean()) #欠損値にageの平均値で補完。
# 最頻値はmode()[0] (モードは配列で出力されるので[0]指定する)
#複数のカラム穴埋め
df_tobacco.fillna({'outcome': 'Alive', 'smoker': 'No'})
#前後の値で穴埋め(時系列データ等に使用)
df.fillna(method='ffill')#'ffill'は先頭から末尾↓にNaNを直近までの非欠損値で置換。'bfill'は逆↑

# 計算の際に少しでも欠損値があると結果Nanになる。欠損値を無視するオプションを使う。
df.sum(skipna=False)
df.mean(skipna=False)

重複処理

#重複の確認
df_s.duplicated().value_counts()
df_s[df_s.duplicated() == True].count()
#重複した行をすべて取得
df_titanic_ticket = df_titanic[df_titanic.duplicated(subset='ticket', keep=False)]

#duplicatedはインデックスの重複を調べるのでカラムの重複を調べる場合は行列反転
df.loc[:, ['Close', 'Adj Close']].transpose().duplicated(keep='first')

#重複行の削除
df = df.drop_duplicates()#keep=Falseで重複した行のインデックスラベルが異なる場合でも削除できる

集計

※日付データをindexに指定後処理する
#任意の期間の集計 :W(週)M(月)Q(四半期)Y(年)
df.resample('M').sum()
df.resample('2W').agg(['sum','mean'])

#2017年10月1日以降のデータを抽出
from datetime import datetime
df.loc[df.index > datetime(2017, 10, 1), [カラム名のリスト]]

#グループ化
gb = df_forbes.groupby('Country')#Countryでグループ化
print(gb.ngroups)#グループ数。size()で値ごとの件数
df_forbes_jp = gb.get_group('Japan')#Japanのみ抽出
#Sectorでグループ化してSales列の合計を求める
res1 = df_forbes.groupby('Sector')['Sales'].sum()
#Countryでグループ化してRank列の平均、最高順位、最低順位を求める
res2 = df_forbes.groupby('Country')['Rank'].agg(['mean','min','max'])
#Countryでグループ化してRankの中央値とSalesの標準偏差を求める
res3 = df_forbes.groupby('Country').agg({'Rank':'median','Sales':'std'})
#列Sales, Assets, Market ValueについてCountry毎の合計が1になるよう(各値の割合)に変換
df_norm = df_forbes.groupby('Country')['Sales', 'Assets', 'Market Value'].transform(lambda x: x / x.sum())
#Country別グループについて、列Sectorの欠損率が5%未満のグループを抽出
res1 = df_forbes.groupby('Country').filter(lambda x: x['Sector'].isna().sum() / len(x) < 0.05)#pd.isna()の方が、pd.isnull()よりも最近に追加されたメソッド
#Country別グループについて、列Profitsの値が全て正のグループを抽出
res2 = df_forbes.groupby('Country').filter(lambda x: x['Profits'].min() > 0)

#クロス集計(termインデックス(行)、loan_statusカラム(列)としてクロス集計)
cross_term = pd.crosstab(loans['term'], loans['loan_status'], margins=True)
print(cross_term)

#ピボットテーブル
pivot_table = df.pivot_table( index='商品名', columns='販売店',values='売上金額', aggfunc='sum')
#列ごとに集計関数を適応する場合
df_gapminder.pivot_table(index='year', columns='continent',
                         values=['lifeExp', 'gdpPercap'],
                         aggfunc={'lifeExp': 'min', 'gdpPercap': 'max'})


【ピボットテーブル】
data: ピボットテーブルを作成するデータ
index: ピボットテーブルの行見出し
columns: ピボットテーブルの列見出し
values: ピボットテーブルの値
aggfunc: ピボットテーブルの集計関数

集計関数

sum()関数:データの合計
min()関数:データの最小値
max()関数:データの最大値
mean()関数:データの平均値
std()関数:データの標準偏差
median()関数:データの中央値
mode()関数:データのモード(最も頻度の高い値)
count()関数:欠損値のないデータの個数
unique()関数:データのユニークな値
value_counts()関数:データの値の出現回数

pandas可視化

#最後にplt.show()要

#全ての項目ヒストグラム
df.hist(figsize=(20,20), color='b')
df.plot.hist(bins=40, edgecolor='black')
# dfのage列をヒストグラムで表示
df['age'].hist(color='g')

# df2のname列の要素ごとの3科目合計得点を棒グラフで表示
df2['sum'] = df2.iloc[:,2:5].sum(axis=1) #3科目合計の列を作成
df2[['name','sum']].plot(kind='bar',x=df2.columns[0])
# df2のname列の要素ごとの3科目を棒グラフで並べて表示
df2[['name','English','Mathematics','History']].plot(kind='bar',figsize=(10,4),x=df2.columns[0])#stacked=Trueで積み上げ棒グラフ

#水平棒グラフ
df.plot.barh()

#折れ線グラフ
ser_passengers = df_flights.groupby('year')['passengers'].sum()
ser_passengers.plot()
plt.title('Life expectancy by continent')
plt.xticks(range(1957, 2012+1, 5))#rangeで目盛り変更

#全ての項目散布図
from pandas.plotting import scatter_matrix
df2 = scatter_matrix(df,figsize=(20,20))
#ageとfareで散布図
df.plot(kind='scatter',x='age',y='fare',figsize=(8,6),title='age-fare scatter')

CSV出力

#to_csvでcsv形式で出力
#行番号、列名を削除して出力したいときはindex=None,header=Noneをつける
df_copy = df[['name','age','sex']]
df_copy.to_csv('../output/sample.csv')
df_copy.to_csv('../output/sample.csv',index=None,header=None)


いいなと思ったら応援しよう!