自分のキャリアをあれこれ考えながら、Pythonで様々なデータを分析していくブログです

[pandas] その1 行と列の選択でのデータ抽出の方法をまとめてみた

Python
Python
ヒノマルク
ヒノマルク

pandasの使い方をまとめていきます。
まずは行と列のデータの抽出方法です。
中々忘れがちですよね。pandasのバージョンによって非推奨になる機能もあります。

事前に下記記事ご覧になっておくとより理解が深まると思います。

[Python] Numpyとは何かまとめてみました。
ヒノマルク pandasの使い方を書く前に、numpyの知識も必要だと思い調べました。 自分があまり理解できていなかったことを痛感しました。 記事をまとめるのにそれなりに時間がかかりましたが、勉強になりました。 Numpyとは何か Nump...

条件式での特定行の抽出方法をお探しの方は下記にまとまっています。

[pandas] その2 条件式でのデータ抽出方法をまとめてみた
ヒノマルク 前回は行の番号や列のラベルでデータを抽出していました。 今回はセルの値を条件にして抽出する方法を調べて見ます。 下記記事の続きになります。 Boolean Indexing (ブール索引) によるデータ抽出 ある行や列を特定の値...
スポンサーリンク

pandasで行列指定によるデータの抽出方法

下記2つの方法があるようです。ヒノマルクは今までずっと1番の方法を使ってました。

2番の方法が推奨されているとは知らず、これからは.atや.locを使おうと思います。

  1. Python標準もしくはNumpyのindex操作 [] や属性操作 . の利用

The Python and NumPy indexing operators [] and attribute operator . provide quick and easy access to pandas data.
引用:https://pandas.pydata.org/docs/user_guide/indexing.html#indexing

  1. 最適化されたpandasのデータ操作メソッド (.at、.iat、.loc 、.iloc)の利用 (production向けのコードでの利用を推奨)

for production code, we recommend the optimized pandas data access methods, .at, .iat, .loc and .iloc.
引用: https://pandas.pydata.org/docs/user_guide/10min.html#selection

スポンサーリンク

データ抽出方法のまとめ

今回はボストンの住宅価格のデータセットを使ってデータフレームからデータを抽出していこうと思います。

ボストンの住宅価格のデータセットとは何?という方は下記記事をご覧ください。
(ヒノマルクの旧ブログになります。)

ボストンの住宅価格を単回帰分析で予測してみた - (旧) ヒノマルクのデータ分析ブログ
前回数値予測が可能なサンプルデータセットをやろうと記事にしていましたが、 候補データを選定しました。町の住宅価格の中央値を周辺環境から予測するといった 回帰分析向けのサンプルデータになります。場所はボストンで1970年代とかなり古めのデータ...
pandasのバージョンの確認
import pandas as pd
pd.__version__
Out[0]
'1.3.5'
本日の記事はpandasのバージョン1.3.5にて動作確認をしています。
異なるバージョンだと挙動が違う可能性がありますのでご留意ください。

それではpandasでデータ抽出する方法を確認していきます。

ボストンの住宅価格データセットの読み込み
df = pd.read_csv("http://lib.stat.cmu.edu/datasets/boston_corrected.txt", encoding='Windows-1252',skiprows=9,sep="\t")
上から5件を取得
df.head()
Out[0]
OBS. TOWN TOWN# TRACT LON LAT MEDV CMEDV CRIM ZN ... CHAS NOX RM AGE DIS RAD TAX PTRATIO B LSTAT
0 1 Nahant 0 2011 -70.955 42.2550 24.0 24.0 0.00632 18.0 ... 0 0.538 6.575 65.2 4.0900 1 296 15.3 396.90 4.98
1 2 Swampscott 1 2021 -70.950 42.2875 21.6 21.6 0.02731 0.0 ... 0 0.469 6.421 78.9 4.9671 2 242 17.8 396.90 9.14
2 3 Swampscott 1 2022 -70.936 42.2830 34.7 34.7 0.02729 0.0 ... 0 0.469 7.185 61.1 4.9671 2 242 17.8 392.83 4.03
3 4 Marblehead 2 2031 -70.928 42.2930 33.4 33.4 0.03237 0.0 ... 0 0.458 6.998 45.8 6.0622 3 222 18.7 394.63 2.94
4 5 Marblehead 2 2032 -70.922 42.2980 36.2 36.2 0.06905 0.0 ... 0 0.458 7.147 54.2 6.0622 3 222 18.7 396.90 5.33

5 rows × 21 columns

DataFrameのカラム(列)を確認
df.columns
Out[0]
Index(['OBS.', 'TOWN', 'TOWN#', 'TRACT', 'LON', 'LAT', 'MEDV', 'CMEDV', 'CRIM',
           'ZN', 'INDUS', 'CHAS', 'NOX', 'RM', 'AGE', 'DIS', 'RAD', 'TAX',
           'PTRATIO', 'B', 'LSTAT'],
          dtype='object')
スポンサーリンク

Python標準もしくはNumpyのindex操作 [ ] や属性操作 . の利用方法

DataFrameの1列を選択。今回はTOWN(町名)を取得する
df["TOWN"] # or df.TOWN
Out[0]
0          Nahant
1      Swampscott
2      Swampscott
3      Marblehead
4      Marblehead
          ...
501      Winthrop
502      Winthrop
503      Winthrop
504      Winthrop
505      Winthrop
Name: TOWN, Length: 506, dtype: object

TOWN(町名)の一覧が出せました。

抽出したデータの型を調べる
print(type(df["TOWN"]))
print(type(df.TOWN))
Out[0]
class 'pandas.core.series.Series'
class 'pandas.core.series.Series'

Seriesのようです。

DataFrameはSeriesの集まりだと考えると、1つのカラムに限定した時はSeriesになると思うと理解しやすいかも知れません。

1つのラベルを格納したlistを渡すとどうなるのか
# リストを渡す
df[["TOWN"]]
Out[0]
TOWN
0 Nahant
1 Swampscott
2 Swampscott
3 Marblehead
4 Marblehead
... ...
501 Winthrop
502 Winthrop
503 Winthrop
504 Winthrop
505 Winthrop

506 rows × 1 columns

型の確認
type(df[["TOWN"]])
Out[0]
pandas.core.frame.DataFrame

なんと、DataFrameになりました。リストを渡すとDataFrameが返ってくるようです。

スポンサーリンク

[ ]とスライスによる行の抽出

スライスによる行の抽出
df[1:3]
Out[0]
OBS. TOWN TOWN# TRACT LON LAT MEDV CMEDV CRIM ZN ... CHAS NOX RM AGE DIS RAD TAX PTRATIO B LSTAT
1 2 Swampscott 1 2021 -70.950 42.2875 21.6 21.6 0.02731 0.0 ... 0 0.469 6.421 78.9 4.9671 2 242 17.8 396.90 9.14
2 3 Swampscott 1 2022 -70.936 42.2830 34.7 34.7 0.02729 0.0 ... 0 0.469 7.185 61.1 4.9671 2 242 17.8 392.83 4.03

2 rows × 21 columns

結果を確認すると、1 <= index < 3 のindex行を取得するという意味になるようです。

後述しますが、.loc[1:3]だと 1<= index <=3 という意味合いで抽出されるようなので注意が必要です。
型の確認
type(df[1:3])
Out[0]
pandas.core.frame.DataFrame

DataFrameが返却されました。

ということは、下記のような書き方もできるはず。

行1,2を取得し、TOWN列のみに限定
df[1:3]["TOWN"]
Out[0]
1    Swampscott
2    Swampscott
Name: TOWN, dtype: object

できました。何となく理解できてきました。

お馴染みの型の確認
type(df[1:3]["TOWN"])
Out[0]
pandas.core.series.Series

今度はSeriesになりました。1列に絞れたからでしょうか。

1列だけではない場合はどうなるのか試して見ます。

行1,2を取得し、TOWN・LAT・LON列のみに限定
df[1:3][["TOWN","LAT","LON"]]
Out[0]

TOWN LAT LON
1 Swampscott 42.2875 -70.950
2 Swampscott 42.2830 -70.936

取得できました。

型の確認
type(df[1:3][["TOWN","LAT","LON"]])
Out[0]
pandas.core.frame.DataFrame

DataFrameが返却されました。
1列取得の場合、Series。複数列取得の場合、DataFrameとして返却されるようです。

今までよく考えずに使ってきましたが、結果をみると違いがあったようです。

確認していなかったのでおまけで1行・複数列の場合どうなるのかも見てみます。

1行・複数列の抽出
df[0:1]
Out[0]
OBS. TOWN TOWN# TRACT LON LAT MEDV CMEDV CRIM ZN ... CHAS NOX RM AGE DIS RAD TAX PTRATIO B LSTAT
0 1 Nahant 0 2011 -70.955 42.255 24.0 24.0 0.00632 18.0 ... 0 0.538 6.575 65.2 4.09 1 296 15.3 396.9 4.98

1 rows × 21 columns

型の確認
type(df[0:1])
Out[0]
pandas.core.frame.DataFrame

スライスを使う方法だと1行複数列取得の場合、DataFrameが返却されるようです。

こちらも後述しますが、.locで1行複数列の取得をするとSeriesで返却されます。
スポンサーリンク

最適化されたpandasのデータ操作メソッド (.at、.iat、.loc 、.iloc)の利用

※ 22年2月現在、production向けのコードで利用を推奨されています。

.locの使い方

公式ページにSeriesとDataFrameに対しての操作方法が載っています。

種別 インデックス操作
Series series.loc[indexer]
DataFrame df.loc[row_indexer,column_indexer]

上記の表を念頭に置きつつ確認を進めていきます。

.locで最初の行を取得してみる
df.loc[0]
Out[0]
OBS.             1
TOWN        Nahant
TOWN#            0
TRACT         2011
LON        -70.955
LAT         42.255
MEDV          24.0
CMEDV         24.0
CRIM       0.00632
ZN            18.0
INDUS         2.31
CHAS             0
NOX          0.538
RM           6.575
AGE           65.2
DIS           4.09
RAD              1
TAX            296
PTRATIO       15.3
B            396.9
LSTAT         4.98
Name: 0, dtype: object

取得できました。

型の確認
type(df.loc[0])
Out[0]
pandas.core.series.Series

型はSeriesのようです。

行インデックス0から1を取得
df.loc[0:1]
Out[0]

OBS. TOWN TOWN# TRACT LON LAT MEDV CMEDV CRIM ZN ... CHAS NOX RM AGE DIS RAD TAX PTRATIO B LSTAT
0 1 Nahant 0 2011 -70.955 42.2550 24.0 24.0 0.00632 18.0 ... 0 0.538 6.575 65.2 4.0900 1 296 15.3 396.9 4.98
1 2 Swampscott 1 2021 -70.950 42.2875 21.6 21.6 0.02731 0.0 ... 0 0.469 6.421 78.9 4.9671 2 242 17.8 396.9 9.14

2 rows × 21 columns

2行が抽出されました。

型を確認
type(df.loc[0:1])
Out[0]
pandas.core.frame.DataFrame

DataFrameのようです。
1行だけのときはSeriesで複数行になるとDataFrameになるようです。

行指定なし、列指定あり
df.loc[:, ["TOWN", "LAT", "LON"]]
Out[0]

TOWN LAT LON
0 Nahant 42.2550 -70.9550
1 Swampscott 42.2875 -70.9500
2 Swampscott 42.2830 -70.9360
3 Marblehead 42.2930 -70.9280
4 Marblehead 42.2980 -70.9220
... ... ... ...
501 Winthrop 42.2312 -70.9860
502 Winthrop 42.2275 -70.9910
503 Winthrop 42.2260 -70.9948
504 Winthrop 42.2240 -70.9875
505 Winthrop 42.2210 -70.9825

506 rows × 3 columns

TOWN列〜LON列の抽出
df.loc[:, "TOWN":"LON"]

上記のような書き方も可能なようです。

Out[0]

TOWN TOWN# TRACT LON
0 Nahant 0 2011 -70.9550
1 Swampscott 1 2021 -70.9500
2 Swampscott 1 2022 -70.9360
3 Marblehead 2 2031 -70.9280
4 Marblehead 2 2032 -70.9220
... ... ... ... ...
501 Winthrop 91 1801 -70.9860
502 Winthrop 91 1802 -70.9910
503 Winthrop 91 1803 -70.9948
504 Winthrop 91 1804 -70.9875
505 Winthrop 91 1805 -70.9825

506 rows × 4 columns

TOWN列とLON列の間にTOWN#列があるので、一緒に抽出されています。
まとめて選択したい場合は便利ですね。

さて、ここでまた疑問です。

1カラムだけ選択するとSeriesになるのでしょうか?

TOWN列のみ抽出
df.loc[:, "TOWN"]
Out[0]

0          Nahant
1      Swampscott
2      Swampscott
3      Marblehead
4      Marblehead
          ...
501      Winthrop
502      Winthrop
503      Winthrop
504      Winthrop
505      Winthrop
Name: TOWN, Length: 506, dtype: object
type(df.loc[:, "TOWN"])
Out[0]
pandas.core.series.Series

Seriesで返ってきました。df["TOWN"]と結果は変わらなそうです。

listで列名を渡すとどうなるのか?

type(df.loc[:, ["TOWN"]])
Out[0]
pandas.core.frame.DataFrame

DataFrameで返ってきました。 df[["TOWN"]]と結果は変わりません。

Series + .loc

df.loc[:, "TOWN"] でSeriesが返ってくるので、さらにlocでindexの指定をしてみます。

Seriesからインデックス番号1の値を取得
df.loc[:, "TOWN"].loc[1]
Out[0]
'Swampscott'
type(df.loc[:, "TOWN"].loc[1])
Out[0]
str

str型のようです。

.ilocの使い方

行と列を番号で選択
df.iloc[0:3,0:3]
Out[0]

OBS. TOWN TOWN#
0 1 Nahant 0
1 2 Swampscott 1
2 3 Swampscott 1
type(df.iloc[0:3,0:3])
Out[0]
pandas.core.frame.DataFrame

DataFrameです。

カラムだけ指定
df.iloc[:,0:3]
Out[0]

OBS. TOWN TOWN#
0 1 Nahant 0
1 2 Swampscott 1
2 3 Swampscott 1
3 4 Marblehead 2
4 5 Marblehead 2
... ... ... ...
501 502 Winthrop 91
502 503 Winthrop 91
503 504 Winthrop 91
504 505 Winthrop 91
505 506 Winthrop 91

506 rows × 3 columns

行だけ指定
df.iloc[0:3] # or df.iloc[0:3,:]
Out[0]

OBS. TOWN TOWN# TRACT LON LAT MEDV CMEDV CRIM ZN ... CHAS NOX RM AGE DIS RAD TAX PTRATIO B LSTAT
0 1 Nahant 0 2011 -70.955 42.2550 24.0 24.0 0.00632 18.0 ... 0 0.538 6.575 65.2 4.0900 1 296 15.3 396.90 4.98
1 2 Swampscott 1 2021 -70.950 42.2875 21.6 21.6 0.02731 0.0 ... 0 0.469 6.421 78.9 4.9671 2 242 17.8 396.90 9.14
2 3 Swampscott 1 2022 -70.936 42.2830 34.7 34.7 0.02729 0.0 ... 0 0.469 7.185 61.1 4.9671 2 242 17.8 392.83 4.03

3 rows × 21 columns

Series + .iloc

df.iloc[:,1]でSeriesが返ってくるので、さらにilocでindexの指定をしてみます。

Seriesからインデックス番号1の値を取得
df.iloc[:,1].iloc[1]
Out[0]
'Swampscott'
type(df.iloc[:,1].iloc[1])
Out[0]
str

str型です。

スポンサーリンク

[]や.locを使ってデータを抽出した場合、Seriesが返ってくるのかDataFrameが返ってくるのかのまとめ。

気になったので調べてみたら、stackoverflowにまとめてる方がいました。

Pandas selecting by label sometimes return Series, sometimes returns DataFrame
In Pandas, when I select a label that only has one entry in the index I get back a Series, but when I select an entry th...

上記の内容を参考にしつつ、今回の結果をまとめると下記のようになりました

No 抽出パターン df[]、df. df.loc、df.iloc
1 1行・1列 ・Series
df[0:1]["TOWN"]
・値のデータ型(strなど)
df.loc[0]["TOWN"]
2 1行・1列
(列ラベルがlist)
・DataFrame
df[0:1][["TOWN"]]
・Series
df.loc[0][["TOWN"]]
3 複数行・1列 ・Series
df["TOWN"]
・Series
df.loc[:,"TOWN"]
4 複数行・1列
(列ラベルがlist)
・DataFrame
df[["TOWN"]]
・DataFrame
df.loc[:,["TOWN"]]
5 1行・複数列 ・DataFrame
df[0:1]
・Series
df.loc[0]
6 1行・複数列
(列ラベルがlist)
・DataFrame
df[0:1][["LAT","LON"]]
・Series
df.loc[0][["LAT","LON"]]
7 複数行・複数列 ・DataFrame
df[0:9]
・DataFrame
df.loc[0:9]
8 複数行・複数列
(列ラベルがlist)
・DataFrame
df[0:9][["LAT","LON"]]
・DataFrame
df.loc[0:9][["LAT","LON"]]

表にしてみると分かりやすいですが、1行のみを抽出した場合の結果がlocを使う場合と使わない場合で異なるようです。

スポンサーリンク

.atの使い方

Excelで例えると1つのセルの値を取得するメソッドのようです。
1つの値を取得するだけであれば、.atもしくは.iatを使うのが最も速いようです。

If you only want to access a scalar value, the fastest way is to use the at and iat methods, which are implemented on all of the data structures.
引用: https://pandas.pydata.org/docs/user_guide/indexing.html#indexing-label

行列を指定し抽出 (列がラベル)
df.at[0,"TOWN"]
Out[0]
'Nahant'

抽出できました。

type(df.at[0,"TOWN"])
Out[0]
str

str型になっています。

行列を指定し抽出 (列が番号)
df.iat[0,1]
Out[0]
'Nahant'

結果は.atを使った場合と同じです。

type(df.iat[0,1])
Out[0]
  str

返却される値の型も同じ

スポンサーリンク

まとめ

今まで特に考えなくても使えていたpandasですが、調べてみると抽出方法にも複数のやり方があることがわかりました。これからはproductionで推奨されているlocなどを使って抽出用コードを書くことにします。

スポンサーリンク

参照

https://pandas.pydata.org/docs/user_guide/10min.html#selection
https://www.geeksforgeeks.org/creating-a-dataframe-from-pandas-series/
https://stackoverflow.com/questions/10665889/how-to-take-column-slices-of-dataframe-in-pandas
https://pandas.pydata.org/docs/reference/api/pandas.DataFrame.at.html

タイトルとURLをコピーしました