#author("2023-12-13T10:56:00+09:00;2021-12-06T13:12:50+09:00","default:inoue.ko","inoue.ko") #author("2023-12-13T18:41:02+09:00;2021-12-06T13:12:50+09:00","default:inoue.ko","inoue.ko") *Pandas https://pandas.pydata.org/ ~ Pandas は、データの読込、集計、組み替えなどを行うことができる [[Python]]プログラミングにおけるデータ解析の定番ライブラリです。.csv、.xlsx 他、多様な形式かつ、大量のデータの読み込みに対応しており、表計算ソフトよりも高速で処理を行うことができます。 Pandas が扱うデータ構造には、''Series''(1次元)と ''DataFrame''(2次元)がありますが、重要なのは ''DataFrame'' で、表構造の組み替え・抽出、統計解析、グラフ化など、様々な手法を理解することで、実践的なデータ解析ができるようになります。 以下、公式のチートシートで全体像を把握できます。 https://pandas.pydata.org/Pandas_Cheat_Sheet.pdf ~ ***CONTENTS #contents2_1 ~ ~ **はじめに ***Pandasのインストールとインポート Python 言語のライブラリとしてのインストールになるので、一般の Python3 の環境であれば、Terminalから以下のコマンドでインストールできます。 $ pip3 install pandas import する際は、以下のように ''pd'' という名称を与えるのが一般的です。 import pandas as pd ''Google Colaboratory では'' Jupyter Notebook で利用できるライブラリーが「すべてインストール済み」という前提なので、ローカル環境での作業のように、必要なライブラリのインストールを行う必要はなく、コードセルに import 文を書くだけで使うことができます。 ~ ***Pandas のキーワード Pandasが扱うデータ構造には、以下の2つがあります。 -Series:1次元のデータ構造 -DataFrame:2次元の表型のデータ構造 Pandas では、以下の2つのラベルでデータを特定します。 -index:行方向のラベル -columns:列方向のラベル( Series には存在しません) ~ ~ **Seriesとは Series は Pandas の1次元データ構造で、''data'' と ''index'' の2つの要素から構成されます。 -data:個々のデータ値 -index:ラベル(指定されない場合は 0, 1, 2,・・・) 以下のように data と index を定義します。 ser = pd.Series( data = [ 60, 80, 70 ] , index=['Alice','Bob','Charlie'] ) ser ----------------- Alice 60 Bob 80 Charlie 70 データ部分のみを定義した場合、index は自動的に 0, 1, 2・・となります。 ser = pd.Series( [ 60, 80, 70 ] ) ser ----------------- 0 60 1 80 2 70 データは、ラベル(index)を使って抽出することができます。 ser['Bob'] ----------------- 80 その他様々な操作が可能ですが、実際には2次元の表構造である DataFrame の活用が中心になりますので、ここでは省略します(以下、公式文書)。 https://pandas.pydata.org/docs/reference/api/pandas.Series.html ~ ~ **DataFrame とは The primary pandas data structure. Two-dimensional size-mutable, potentially heterogeneous tabular data structure with labeled axes (rows and columns). Arithmetic operations align on both row and column labels. Can be thought of as a dict-like container for Series objects. DataFrame とは、Pandasライブラリ扱う「主要なデータ構造」で、以下のような特徴を持つ[[オブジェクト>ObjectOrientedProgramming]]です。 -2次元の大きさが変更できる表形式データ -列ごとに様々な形式のデータが格納される -各次元(行と列)方向に、ラベリングされた軸が存在する -各次元(行と列)方向に数学的な操作を行うことができる -Seriesオブジェクトを辞書のような形式で格納できるオブジェクト DataFrame は ''data''、''index''、''columns'' の3つの要素から構成されます。 -data:表内の個々のデータ値(行列データ) -index:行ラベル(指定されない場合は 0, 1, 2,・・・) -columns:列ラベル(指定されない場合は 0, 1, 2,・・・) 以下、DataFrame を扱う変数として ''df'' を使って事例を紹介します。 ''df'' は DataFrame の略として一般によく用いられる変数名です。 ~ ~ **DataFrame の定義 一般に、データフレームは CSV や Excel 等のデータを読み込んで生成するものですが(後述)、ここではデータフレームの構造を理解する意味で、直接的なデータの定義方法について紹介します。 pd.DataFrame( data, index, columns, dtype, copy) ~ ***定義例1 import pandas as pd df = pd.DataFrame( [ [ '佐藤', 170, 60 ] , ['田中', 160, 50 ] , [ '鈴木', 165, 58 ] ] ) df 0 1 2 0 佐藤 170 60 1 田中 160 50 2 鈴木 165 58 行ラベル、列ラベル(index と columns ) には、自動的に番号数字が入ります。 ~ ***定義例2 import pandas as pd df = pd.DataFrame( data =[ [ '佐藤', 170, 60 ] , ['田中', 160, 50 ] , [ '鈴木', 165, 58 ] ] , index = ['S01', 'S02', 'S03'], columns =[ 'name' , 'height', 'weight'] ) df name height weight S01 佐藤 170 60 S02 田中 160 50 S03 鈴木 165 58 ~ ***定義例3 numpy の2次元配列を使って定義する事例 import pandas as pd import numpy as np df = pd.DataFrame( data = np.array( [ [10, 20, 30, 40], [11, 21, 31, 41], [12, 22, 32, 42] ] ), index = [ 'row_1', 'row_2', 'row_3' ] , columns = [ 'col_1', 'col_2', 'col_3', 'col_4' ] ) df col_1 col_2 col_3 col_4 row_1 10 20 30 40 row_2 11 21 31 41 row_3 12 22 32 42 ~ ***定義例4 0から11までの数値を 3行・4列にあてはめて定義する事例 import pandas as pd import numpy as np df = pd.DataFrame( data = np.arange(12).reshape(3, 4), index = [ 'row_1', 'row_2', 'row_3' ], columns = [ 'col_1', 'col_2', 'col_3', 'col_4' ] ) df col_1 col_2 col_3 col_4 row_1 0 1 2 3 row_2 4 5 6 7 row_3 8 9 10 11 ~ ~ **DataFrame へのファイルの読込と基本情報 ***データファイルの読み込み -CSV形式のデータの読込:read_csv() # iris.csv の読み込み df = pd.read_csv("/path/to/iris.csv") -Excelファイルの読込:read_excel() [[GoogleColaboratory]] で GoogleDriveにあるExcelファイルを読む事例 df = pd.read_excel('/content/drive/My Drive/path/to/sample.xlsx') -JSON文字列の読込:read_json() ~ ***基本情報の表示 -データ全体の表示:DataFrame df -データの基本情報 --データの特徴表示:DataFrame.info( ) --データの一部を表示:DataFrame.head( )、DataFrame.tail( ) --データの形状(行数・列数)を表示:DataFrame.shape --行数を取得: len(DataFrame) --列数を取得: len(DataFrame.columns) --列名を取得:DataFrame.columns.values --それぞれの列の型を取得:DataFrame.dtypes -''要約統計量の表示'':DataFrame.describe( ) df.describe( ) データ件数、平均、標準偏差、最大・最小等の統計量が一括表示されます。 -''相関行列の表示'':DataFrame.corr( ) df.corr(numeric_only=True) 数値項目間の相関が、カラム数 x カラム数 のマトリックスで表示されます。 ''付記'':df.corr() だけでも結果は表示されますが、最近のアップデートで、"numeric_only=True" つまり、「数値項目のみを対象」の明示が推奨されています。 ~ ***行・列数の最大値 DataFrame.set_option( ) 処理の実行後の表示では、行・列数が制限されていて、データ数が多い場合は、中途が・・・と省略表示になります。これを回避するためのメソッド。 -最大表示行数の指定(ここでは50行を指定 ) pd.set_option( 'display.max_rows', 50 ) ~ ~ **DataFrame からのデータの抽出 ***列名の抽出 df.columns ← オブジェクトとして取り出し df.columns.values ← リストとして取り出し ~ ***行名 の抽出 df.index ← オブジェクトとして取り出し df.index.values ← リストとして取り出し ~ ***データの抽出 DataFrame[ ]、DataFrame.iloc[ ]、DataFrame.loc[ ] 以下、いずれも抽出後の DataFrame を返しますが、元のデータフレームそのものが更新されることはありません(非破壊操作)。 -ここでは DataFrame(df)を以下のようなものとして説明します。 col_1 col_2 col_3 col_4 row_1 10 20 30 40 row_2 11 21 31 41 row_3 12 22 32 42 : : : : : -列の抽出 --col_1 列の抽出 df[ 'col_1 ' ] または df.col_1 --複数列の抽出 df[ [ 'col_2 ' , 'col_3 ' ] ] -行の抽出 --先頭から 3 行目までを抽出 df[ :3 ] # 0, 1, 2 行目が抽出されます --特定区間を抽出 df[ 3:5 ] # 3, 4 行目が抽出されます -行・列の番号を利用した抽出( iloc属性の利用 ) --1行目・1列目のデータを抽出 df.iloc[ 1 , 1 ] --すべての行の 5列目 を抽出(「 : 」 は「すべて取り出す」の意味) df.iloc[ : , 5 ] --すべての行の 5列目から10列目を抽出 df.iloc[ : , 5:10] --すべての列の 5行目から10行目を抽出 df.iloc[ 5:10, : ] --すべての行の 特定の列区間を抽出 df.iloc[ : , :3] # 0, 1, 2列目が抽出されます df.iloc[ : , 5:10] # 5, 6, 7, 8, 9列目が抽出されます --すべての列の 特定の行区間を抽出 df.iloc[ :3 , : ] # 0, 1, 2行目が抽出されます df.iloc[ 5:10, : ] # 5, 6, 7, 8, 9行目が抽出されます -index・columns名を利用した抽出( loc属性の利用 ) --row_1行の col_2 のデータを抽出 df.loc[ 'row_1' , 'col_2' ] --すべての行の col_2 を抽出(「 : 」 は「すべて取り出す」の意味) df.loc[ : , 'col_2' ] --単一インデックス + 複数カラム指定で抽出 df.loc['row_1', [ 'col_1', 'col_2' ] ] --インデックス範囲指定 + 単一カラム指定で抽出 df.loc[ 'row_2' : 'row_10' , 'col_1'] df.loc[ 10 : 19 , 'col_1' ] ~ ***query メソッドによるデータの抽出 DataFrame.query( ) 以下、いずれも抽出後の DataFrame を返しますが、元のデータフレームそのものが更新されることはありません(非破壊操作)。 -height列の値が 170以下である行を抽出 df.query( 'height <= 170' ) -A列の値が B列の値より大きい行を抽出 df.query( 'A > B' ) -name列の値が文字列 "鈴木" である行を抽出 df.query( ' name == "鈴木" ' ) -芸術学部で GPA が 2.0 以上を抽出 df.query( ' 学部 == "芸術" and GPA >= 2.0 ' ) -参考:条件式に使える記号 等 号:== 否 定:!= 不等号:< , > , <= , >= 論理積:and , & 論理和:or , | ~ ~ **DataFrame のリシェイプ ***データフレームのメルト pd.melt( ) -列をカテゴリーにして行方向へ展開 #image(melt.jpg,right,30%) pd.melt( df ) #clear ~ ***データフレームのピボット pd.pivot( ) -カテゴリーを列方向へ展開 #image(pivot.jpg,right,30%) pd.pivot( columns = 'XXX' , values = 'YYY' ) #clear ~ ***データフレームの結合 pd.concat( ) -行方向の結合(例:10行と20行を結合して30行に) pd.concat( [ df1 , df2 ] ) -列方向の結合(例:2列と3列を結合して5列に) pd.concat( [ df1 , df2 ] , axis = 1 ) ~ ~ **DataFrame の操作 ***データの並べ替え DataFrame.sort_values( ) , DataFrame.sort_index( ) -値による並べ替え:DataFrame.sort_values( ) # col_2 の降順に並べ替え df.sort_values( 'col_2', ascending=False ) -インデックスによる並べ替え:DataFrame.sort_index( ) # インデックスの降順に並べ替え(デフォルトは ascending = True ) df.sort_index( ascending = False ) ~ ***行・列名の変更 DataFrame.rename( ) rename メソッドの引数 index および columns に、{ 元の値 : 新しい値 } のかたちで(辞書型の定義)で元の値と新しい値を指定します。デフォルトでは変更後の状態を返すだけで、元の DataFrame そのものを破壊・更新するわけではありません。引数 inplace = True にすると、元の DataFrame が変更されます。 -行名の変更 df2 = df.rename( index={ 'row_1': 'ONE' } ) # 元の df は破壊されません。 df.rename( index={ 'row_1': 'ONE'} , inplace=True ) # df自体が更新されます。 -列名の変更 df2 = df.rename( columns = { 'col_1' : 'A' } ) -複数まとめて指示する場合は、カンマで区切って列挙します。 df2 = df.rename( index = { 'row_1' : 'ONE' } , columns = { 'col_1' : 'A' } ) -index がデフォルトの場合は、' ' を使わず、数字を指定します。 df2 = df.rename( index = { 1 : 'ONE' } , columns = { 'col_1' : 'A' } ) ~ ***行・列の削除 DataFrame.drop( ) dropメソッドは、デフォルトでは削除後の状態を返すだけで、元のデータフレームそのものを破壊・更新するわけではありません。引数 inplace = True にすると、元の DataFrame が変更されます。 -特定行の削除の事例 df2 = df.drop( index='row_2' ) #元の df は破壊されません。 df.drop( index='row_2', inplace=True ) #df 自体が更新されます。 -複数行の削除(リストで指定)の事例 df2 = df.drop( index = [ 'row_1' , 'row_3' ] ) -特定列の削除の事例 df2 = df.drop( columns = 'col_2' ) -複数列の削除(リストで指定)の事例 df2 = df.drop( columns=[ 'col_1' , 'col_3' ] ) -行列まとめて削除指定する場合は、カンマで区切って列挙します。 df2 = df.drop( index='row_3' , columns='col_2' ) ~ ***欠損値に関する処理 DataFrame.isnull( ) , DataFrame.dropna( ) , DataFrame.fillna( ) 実際のデータには、無回答や入力ミスなどで欠損値が発生することがあります。pandas では NaN(Not a Number:非数) と表記され部分が欠損値ですが、これは後の演算等でエラーを発生させる原因となるので、それを含む部分は除去することが必要です(データクレンジング)。 -欠損値の確認:DataFrame.isnull( ).sum( ) df.isnull( ).sum( ) -欠損値のある行を削除:DataFrame.dropna( ) df = df.dropna( how='any' ) &small(※ how ='any' は欠損がひとつでもあれば、その行を削除); -欠損値に値を埋める:DataFrame.fillna( ) ~ ~ **DafaFrame における統計処理 ***カウント・合計・平均・中央値・分散 DataFrame.value_counts( ) , DataFrame.sum(numeric_only=True ) , DataFrame.mean(numeric_only=True), DataFrame.median(numeric_only=True) , DataFrame.var(numeric_only=True) -値ごとの数をカウント df[ '学部' ].value_counts( ) # 全データ中の各学部の人数をカウント -列の合計・平均・分散 df[ 'col_1' ].sum( ) # col_1列の合計 df[ 'col_1' ].mean( ) # col_1列の平均 df[ 'col_1' ].median( ) # col_1列の中央値 df[ 'col_1' ].var( ) # col_1列の分散 ~ ***GroupByオブジェクトの利用 DataFrame.groupby( ) 特定の列データの値を基準にグルーピングした上で、グループごとの統計処理を行うことができます。例えば、全学生の成績一覧がある場合に、学部ごとの成績の平均を求める・・といったことが可能です。 -グループごとの統計 df.groupby( '学部' ).sum( numeric_only=True ) df.groupby( '学部' ).mean( numeric_only=True ) df.groupby( '学部' ).var( numeric_only=True ) -グループを複合して統計 # 各学部 X 性別ごとの平均 df.groupby( [ '学部' ,'性別' ] ).mean( numeric_only=True ) ~ ***クロス集計 pandas.crosstab( ) 2項目のカテゴリの組み合わせでサンプル数の算出ができます。 第一引数が index(行見出し)、第二引数が columns(列見出し)となる DataFrame が返されます。 -単純クロス集計 pd.crosstab( df[ 'col_1' ] , df[ 'col_2' ] ) -正規化 pd.crosstab( df[ 'col_1' ] , df[ 'col_2' ] , normalize='index' ) ~ ***ピボットテーブルの利用 pandas.pivot_table( index = , columns = , values = , aggfunc = ) 2項目のカテゴリの組み合わせで、データの統計量(平均、合計、最大、最小、標準偏差など)を確認・分析できます。 -各パラメータの意味 --index:元データの列名を指定。結果の行見出しになります。 --columns:元データの列名を指定。結果の列見出しになります。 --values:統計量の対象となる項目 --aggfunc:求める統計量( デフォルトは 平均 mean ) -ピボットテーブルを使った平均値の表示例 pivot_table = pd.pivot_table( df, index='学部', columns='性別' , values='成績' , aggfunc='mean' ) 学部 x 性別 ごとの成績の平均値が表示されます。 // https://pandas.pydata.org/docs/reference/api/pandas.pivot_table.html ~ ~ **Pandas の可視化メソッド pandas のメソッドを用いたのグラフ描画の方法を紹介します(pandas は matplotlib を用いてグラフを描画しています)。 ~ ***ライブラリの読み込み 最低限必要なのは、matplotlib と pandas です。以下典型的な記載例です。 import matplotlib.pyplot as plt import pandas as pd ~ ***ヒストグラム:DataFrame.hist( ) #image(hist.png,right,30%) ヒストグラム(柱状グラフ、度数分布図)は、横軸に階級、縦軸に度数をとった統計グラフで、データの分布状況を可視化するために用いられます。 -すべての数値項目について一括表示 df.hist( ) -生成される画像のサイズを 9インチ x 6インチに指定する場合 df.hist( figsize = ( 9, 6 ) ) -50件分表示の場合 df[:50].hist( figsize = ( 9, 6 ) ) ~ ***ボックスプロット:DataFrame.boxplot( ) #image(box.png,right,30%) ボックスプロット(箱ひげ図)は、データのばらつきを可視化する統計グラフで、箱(box)と、その両側に出たひげ(whisker)で表現されることからその名が付けられています。 一般に以下の五数が要約(five-number summary)されて表示されます。 -最小値(minimum) -第1四分位点(lower quartile) -中央値(第2四分位点、median) -第3四分位点(upper quartile) -最大値(maximum) 以下のコードは、対象カラム(XXX)をグループ(AAA)別に表示します。 df.boxplot( column='XXX' , by='AAA' ) ~ ***散布図:DataFrame.plot.scatter( ) #image(scatter.png,right,30%) 散布図(scatter plot)は、縦軸、横軸に2つの量的データ項目を対応させて、各レコードのデータを点でプロットしたものです。項目間の相関の有無が可視化されます。 以下のコードは、x軸とy軸にカラム項目を指定して表示します。 df.plot.scatter( x='XXX', y='YYY' ) ~ ***折れ線グラフ・棒グラフ・円グラフ これらのグラフは項目間の推移や比較を見るために使います。数万件もある全レコードを表示しても視覚的に読み取ることはできないので、グラフにしたい内容によって、事前に「データの平均値を出す」、「クロス集計する」といった処理を行った後、それを新たなデータフレームとしてグラフの表示に利用するのが一般的です(以下、棒グラフの事例です)。 # 性別ごとに国語・英語・数学の平均値を算出 df_mean = df.groupby( 'Gender' , as_index=False ).mean( ) df_mean ||Gender|Japanese|English|Mathematics|h |0|F|62.350000|58.475000|39.800000| |1|M|56.420455|41.142045|39.539773| #image(bar.png,right,30%) 生成された df_mean を使うと、簡単に積み重ね棒グラフができます。 df_mean.plot.bar( stacked=True ) &small(stacked=True は、積み重ねを有効にする・・という意味です。); ~ ***汎用グラフ:DataFrame.plot( ) &small(引数kindでグラフの種類を指定できます。); -line : 折れ線グラフ(line plot) df.plot( kind='line' ) -bar : 垂直棒グラフ(vertical bar plot) -barh : 水平棒グラフ(horizontal bar plot) -box : 箱ひげ図(boxplot) -hist : ヒストグラム(histogram) -kde, density : カーネル密度推定(Kernel Density Estimation plot ) -area : 面グラフ(area plot) -scatter : 散布図(scatter plot) df.plot(kind='scatter', x='item01', y='item02', alpha=0.5, figsize=(9, 6) ) -hexbin : hexbin plot -pie : 円グラフ(pie plot) ~ ~ **APPENDIX ***sklearn ライブラリのサンプルデータを読む事例 California Housing(カリフォルニアの住宅価格)のデータを読む事例です。 # サンプルデータセットを取得 from sklearn.datasets import fetch_california_housing data_housing = fetch_california_housing() # 読み込んだデータを Pandas の DataFrame型に変換 train_x = pd.DataFrame(data_housing.data, columns=data_housing.feature_names) # 目的変数をDataFrameへ追加 train_y = pd.Series(data_housing.target, name="target") # 先頭の5件を表示 train_x.head() ~ ~ ~