pandasの使い方をまとめていきます。
まずは行と列のデータの抽出方法です。
中々忘れがちですよね。pandasのバージョンによって非推奨になる機能もあります。
事前に下記記事ご覧になっておくとより理解が深まると思います。
条件式での特定行の抽出方法をお探しの方は下記にまとまっています。
pandasで行列指定によるデータの抽出方法
下記2つの方法があるようです。ヒノマルクは今までずっと1番の方法を使ってました。
2番の方法が推奨されているとは知らず、これからは.atや.locを使おうと思います。
- 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
- 最適化された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
データ抽出方法のまとめ
今回はボストンの住宅価格のデータセットを使ってデータフレームからデータを抽出していこうと思います。
ボストンの住宅価格のデータセットとは何?という方は下記記事をご覧ください。
(ヒノマルクの旧ブログになります。)
import pandas as pd
pd.__version__
'1.3.5'
異なるバージョンだと挙動が違う可能性がありますのでご留意ください。
それではpandasでデータ抽出する方法を確認していきます。
df = pd.read_csv("http://lib.stat.cmu.edu/datasets/boston_corrected.txt", encoding='Windows-1252',skiprows=9,sep="\t")
df.head()
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
df.columns
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操作 [ ] や属性操作 . の利用方法
df["TOWN"] # or df.TOWN
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))
class 'pandas.core.series.Series' class 'pandas.core.series.Series'
Seriesのようです。
DataFrameはSeriesの集まりだと考えると、1つのカラムに限定した時はSeriesになると思うと理解しやすいかも知れません。
# リストを渡す
df[["TOWN"]]
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"]])
pandas.core.frame.DataFrame
なんと、DataFrameになりました。リストを渡すとDataFrameが返ってくるようです。
[ ]とスライスによる行の抽出
df[1:3]
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行を取得するという意味になるようです。
type(df[1:3])
pandas.core.frame.DataFrame
DataFrameが返却されました。
ということは、下記のような書き方もできるはず。
df[1:3]["TOWN"]
1 Swampscott 2 Swampscott Name: TOWN, dtype: object
できました。何となく理解できてきました。
type(df[1:3]["TOWN"])
pandas.core.series.Series
今度はSeriesになりました。1列に絞れたからでしょうか。
1列だけではない場合はどうなるのか試して見ます。
df[1:3][["TOWN","LAT","LON"]]
TOWN LAT LON 1 Swampscott 42.2875 -70.950 2 Swampscott 42.2830 -70.936
取得できました。
type(df[1:3][["TOWN","LAT","LON"]])
pandas.core.frame.DataFrame
DataFrameが返却されました。
1列取得の場合、Series。複数列取得の場合、DataFrameとして返却されるようです。
今までよく考えずに使ってきましたが、結果をみると違いがあったようです。
確認していなかったのでおまけで1行・複数列の場合どうなるのかも見てみます。
df[0:1]
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])
pandas.core.frame.DataFrame
スライスを使う方法だと1行複数列取得の場合、DataFrameが返却されるようです。
最適化されたpandasのデータ操作メソッド (.at、.iat、.loc 、.iloc)の利用
※ 22年2月現在、production向けのコードで利用を推奨されています。
.locの使い方
公式ページにSeriesとDataFrameに対しての操作方法が載っています。
種別 | インデックス操作 |
---|---|
Series | series.loc[indexer] |
DataFrame | df.loc[row_indexer,column_indexer] |
上記の表を念頭に置きつつ確認を進めていきます。
df.loc[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])
pandas.core.series.Series
型はSeriesのようです。
df.loc[0:1]
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])
pandas.core.frame.DataFrame
DataFrameのようです。
1行だけのときはSeriesで複数行になるとDataFrameになるようです。
df.loc[:, ["TOWN", "LAT", "LON"]]
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
df.loc[:, "TOWN":"LON"]
上記のような書き方も可能なようです。
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になるのでしょうか?
df.loc[:, "TOWN"]
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"])
pandas.core.series.Series
Seriesで返ってきました。df["TOWN"]と結果は変わらなそうです。
listで列名を渡すとどうなるのか?
type(df.loc[:, ["TOWN"]])
pandas.core.frame.DataFrame
DataFrameで返ってきました。 df[["TOWN"]]と結果は変わりません。
Series + .loc
df.loc[:, "TOWN"] でSeriesが返ってくるので、さらにlocでindexの指定をしてみます。
df.loc[:, "TOWN"].loc[1]
'Swampscott'
type(df.loc[:, "TOWN"].loc[1])
str
str型のようです。
.ilocの使い方
df.iloc[0:3,0:3]
OBS. TOWN TOWN# 0 1 Nahant 0 1 2 Swampscott 1 2 3 Swampscott 1
type(df.iloc[0:3,0:3])
pandas.core.frame.DataFrame
DataFrameです。
df.iloc[:,0:3]
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,:]
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の指定をしてみます。
df.iloc[:,1].iloc[1]
'Swampscott'
type(df.iloc[:,1].iloc[1])
str
str型です。
[]や.locを使ってデータを抽出した場合、Seriesが返ってくるのかDataFrameが返ってくるのかのまとめ。
気になったので調べてみたら、stackoverflowにまとめてる方がいました。
上記の内容を参考にしつつ、今回の結果をまとめると下記のようになりました
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"]
'Nahant'
抽出できました。
type(df.at[0,"TOWN"])
str
str型になっています。
df.iat[0,1]
'Nahant'
結果は.atを使った場合と同じです。
type(df.iat[0,1])
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