目的
データフレームからの列・行の取り出し方について一通りまとめる。
背景
データを読み込んでデータフレームを作って、特定の行や列を参照したり、書き換えしたりしている。困ったときには、都度ググって対処しているが、基本的な使い方を一通り覚えたく、まとめてみた。
私がよく困ったのは、データフレームからデータを取り出したいのに、Series とか、1行DataFrame として取り出してしまい、参照や書き換えがうまくできないことがよくありました。そもそも、このとり方をすると、どんな型として取得できるのかを理解してなく、混乱し、苦労していました。
そのため、今回、自分のために一通りまとめてみましたが、初学者の方の参考にもなれば幸いです。
サンプルの dataframe を作成
- 3行x3列のサンプルデータフレームを返す関数を作成する。
- データとそのタイプを表示する関数を作成する。タイトルは[]カギカッコ付きで表示する。
import pandas as pd
# サンプルの dataframe を取得
def get_sample_df():
return pd.DataFrame( {'column0':[0,1,2],'column1':[10,11,12],'column2':[20,21,22],}, index=['index0','index1','index2'])
# データとタイプを表示
def print_data_type(title,data):
print(f'[{title}]')
print(data)
print(type(data))
print_data_type('SampleDataFrame',get_sample_df())
[SampleDataFrame]
column0 column1 column2
index0 0 10 20
index1 1 11 21
index2 2 12 22
<class 'pandas.core.frame.DataFrame'>
以下、このサンプルデータフレームを使って、列・行を取り出し、どのようなデータがどのようなデータ型で取得できるかを確認する。
書き換えできるかを確認する際は、わかりやすいように、100(など三桁)に書き換える。
列指定
- カラム名を1個指定すると、 Series が取得できる。
- カラム名のリストを指定すると、複数列のDataFrameが取得できる。
- カラム名のスライスを指定すると、エラーにはならないが、複数列のDataFrame(空)が取得できる。(意味なし)
# カラム名を1個指定
print_data_type('カラム名を1個指定',get_sample_df()['column0'])
[カラム名を1個指定]
index0 0
index1 1
index2 2
Name: column0, dtype: int64
<class 'pandas.core.series.Series'>
# カラム名のリストを指定
print_data_type('カラム名のリストを指定',get_sample_df()[['column0','column2']])
[カラム名のリストを指定]
column0 column2
index0 0 20
index1 1 21
index2 2 22
<class 'pandas.core.frame.DataFrame'>
# カラム名のスライスを指定
print_data_type('カラム名のスライスを指定',get_sample_df()['column0':'column2'])
[カラム名のスライスを指定]
Empty DataFrame
Columns: [column0, column1, column2]
Index: []
<class 'pandas.core.frame.DataFrame'>
行指定
- インデックス名を1個、もしくはリスト指定はできない。
- インデックス名のスライスを指定すると、複数行(連続)のDataFrameが取得できる。
- インデックス名のスライス(同じ名称)を指定すると、1行の DataFrame が取得できる。(DataFrame から1行指定して取得した DataFrame 参照)
# インデックス名のスライスを指定
print_data_type('インデックス名のスライスを指定',get_sample_df()['index0':'index2'])
[インデックス名のスライスを指定]
column0 column1 column2
index0 0 10 20
index1 1 11 21
index2 2 12 22
<class 'pandas.core.frame.DataFrame'>
# インデックス名のスライス(同じ名称)を指定
print_data_type('インデックス名のスライス(同じ名称)を指定',get_sample_df()['index0':'index0'])
[インデックス名のスライス(同じ名称)を指定]
column0 column1 column2
index0 0 10 20
<class 'pandas.core.frame.DataFrame'>
loc
- インデックス名、カラム名を1個指定すると、1要素が取得でき、書き換えできる。
- インデックス名、カラム名とも、リストか、スライスを指定でき、複数行x複数列が取得できる。1行、もしくは、1列の場合は、Series、複数行x複数列の場合は、Dataframe。
- 全列を取得する場合には、カラム名の省略が可能。
- インデックス名とカラム名は、リストとスライスの混在が可能。(インデックス名はリスト、カラム名はスライス。もしくは、その逆)
# インデックス名、カラム名を1個指定
print_data_type('インデックス名、カラム名を1個指定',get_sample_df().loc['index0','column0'])
# 書き換できる
df = get_sample_df()
print_data_type('書き換え前',df)
df.loc['index0','column0'] = 100
print_data_type('書き換え後',df)
[インデックス名、カラム名を1個指定]
0
<class 'numpy.int64'>
[書き換え前]
column0 column1 column2
index0 0 10 20
index1 1 11 21
index2 2 12 22
<class 'pandas.core.frame.DataFrame'>
[書き換え後]
column0 column1 column2
index0 100 10 20
index1 1 11 21
index2 2 12 22
<class 'pandas.core.frame.DataFrame'>
# カラム名を1個指定
print_data_type('カラム名を1個指定',get_sample_df().loc[:,'column0'])
[カラム名を1個指定]
index0 0
index1 1
index2 2
Name: column0, dtype: int64
<class 'pandas.core.series.Series'>
# カラム名をリスト指定
print_data_type('カラム名をリスト指定',get_sample_df().loc[:,['column0','column2']])
[カラム名をリスト指定]
column0 column2
index0 0 20
index1 1 21
index2 2 22
<class 'pandas.core.frame.DataFrame'>
# カラム名をスライス指定
print_data_type('カラム名をスライス指定',get_sample_df().loc[:,'column0':'column2'])
[カラム名をスライス指定]
column0 column1 column2
index0 0 10 20
index1 1 11 21
index2 2 12 22
<class 'pandas.core.frame.DataFrame'>
# インデックス名を1個指定
print_data_type('インデックス名を1個指定',get_sample_df().loc['index0',:])
# インデックス名を1個指定(列は省略可能)
print_data_type('インデックス名を1個指定(列は省略可能)',get_sample_df().loc['index0'])
[インデックス名を1個指定]
column0 0
column1 10
column2 20
Name: index0, dtype: int64
<class 'pandas.core.series.Series'>
[インデックス名を1個指定(列は省略可能)]
column0 0
column1 10
column2 20
Name: index0, dtype: int64
<class 'pandas.core.series.Series'>
# インデックス名をリスト指定
print_data_type('インデックス名をリスト指定',get_sample_df().loc[['index0','index2']])
[インデックス名をリスト指定]
column0 column1 column2
index0 0 10 20
index2 2 12 22
<class 'pandas.core.frame.DataFrame'>
# インデックス名をスライス指定
print_data_type('インデックス名をスライス指定',get_sample_df().loc['index0':'index2'])
[インデックス名をスライス指定]
column0 column1 column2
index0 0 10 20
index1 1 11 21
index2 2 12 22
<class 'pandas.core.frame.DataFrame'>
# インデックス名をスライス指定、カラム名をリスト指定
print_data_type('インデックス名をスライス指定、カラム名をリスト指定',get_sample_df().loc['index0':'index2',['column0','column2']])
[インデックス名をスライス指定、カラム名をリスト指定]
column0 column2
index0 0 20
index1 1 21
index2 2 22
<class 'pandas.core.frame.DataFrame'>
iloc
- インデックス名、カラム名が、行番号、列番号になっただけで、locと同じ。
# 行番号、列番号を1個指定
print_data_type('行番号、列番号を1個指定',get_sample_df().iloc[0,0])
# 書き換できる
df = get_sample_df()
print_data_type('書き換え前',df)
df.iloc[0,0] = 100
print_data_type('書き換え後',df)
[行番号、列番号を1個指定]
0
<class 'numpy.int64'>
[書き換え前]
column0 column1 column2
index0 0 10 20
index1 1 11 21
index2 2 12 22
<class 'pandas.core.frame.DataFrame'>
[書き換え後]
column0 column1 column2
index0 100 10 20
index1 1 11 21
index2 2 12 22
<class 'pandas.core.frame.DataFrame'>
# 列番号を1個指定
print_data_type('列番号を1個指定',get_sample_df().iloc[:,0])
[列番号を1個指定]
index0 0
index1 1
index2 2
Name: column0, dtype: int64
<class 'pandas.core.series.Series'>
# 列番号をリスト指定
print_data_type('列番号をリスト指定',get_sample_df().iloc[:,[0,2]])
[列番号をリスト指定]
column0 column2
index0 0 20
index1 1 21
index2 2 22
<class 'pandas.core.frame.DataFrame'>
# 列番号をスライス指定
print_data_type('列番号をスライス指定',get_sample_df().iloc[:,0:3])
[列番号をスライス指定]
column0 column1 column2
index0 0 10 20
index1 1 11 21
index2 2 12 22
<class 'pandas.core.frame.DataFrame'>
# 行番号を1個指定
print_data_type('行番号を1個指定',get_sample_df().iloc[0,:])
# 行番号を1個指定(列は省略可能)
print_data_type('行番号を1個指定(列は省略可能)',get_sample_df().iloc[0])
[行番号を1個指定]
column0 0
column1 10
column2 20
Name: index0, dtype: int64
<class 'pandas.core.series.Series'>
[行番号を1個指定(列は省略可能)]
column0 0
column1 10
column2 20
Name: index0, dtype: int64
<class 'pandas.core.series.Series'>
# 行番号をリスト指定
print_data_type('行番号をリスト指定',get_sample_df().iloc[[0,2]])
[行番号をリスト指定]
column0 column1 column2
index0 0 10 20
index2 2 12 22
<class 'pandas.core.frame.DataFrame'>
# 行番号をスライス指定
print_data_type('行番号をスライス指定',get_sample_df().iloc[0:3])
[行番号をスライス指定]
column0 column1 column2
index0 0 10 20
index1 1 11 21
index2 2 12 22
<class 'pandas.core.frame.DataFrame'>
# 行番号をスライス指定、列番号をリスト指定
print_data_type('行番号をスライス指定、列番号をリスト指定',get_sample_df().iloc[0:3,[0,2]])
[行番号をスライス指定、列番号をリスト指定]
column0 column2
index0 0 20
index1 1 21
index2 2 22
<class 'pandas.core.frame.DataFrame'>
at
- インデックス名、カラム名を指定して、1要素(1行1列)の取得のみに対応している。
- 機能的には、locの1行1列指定と同じだが、速度が速い。
# インデックス名、カラム名を1個指定
print_data_type('インデックス名、カラム名を1個指定',get_sample_df().at['index0','column0'])
# 書き換できる
df = get_sample_df()
print_data_type('書き換え前',df)
df.at['index0','column0'] = 100
print_data_type('書き換え後',df)
[インデックス名、カラム名を1個指定]
0
<class 'numpy.int64'>
[書き換え前]
column0 column1 column2
index0 0 10 20
index1 1 11 21
index2 2 12 22
<class 'pandas.core.frame.DataFrame'>
[書き換え後]
column0 column1 column2
index0 100 10 20
index1 1 11 21
index2 2 12 22
<class 'pandas.core.frame.DataFrame'>
iat
- インデックス名、カラム名が、行番号、列番号になっただけで、atと同じ。
# 行番号、列番号を1個指定
print_data_type('行番号、列番号を1個指定',get_sample_df().iat[0,0])
# 書き換できる
df = get_sample_df()
print_data_type('書き換え前',df)
df.iat[0,0] = 100
print_data_type('書き換え後',df)
[行番号、列番号を1個指定]
0
<class 'numpy.int64'>
[書き換え前]
column0 column1 column2
index0 0 10 20
index1 1 11 21
index2 2 12 22
<class 'pandas.core.frame.DataFrame'>
[書き換え後]
column0 column1 column2
index0 100 10 20
index1 1 11 21
index2 2 12 22
<class 'pandas.core.frame.DataFrame'>
DataFrame から1列指定して取得した Series
- インデックス名称指定、行番号指定、どちらでも要素を取得できる。(1個、リスト、スライス)
- インデックス名称1個の場合は、.の後にインデックス名称を記述しても取得できる。
- DataFrame から1列指定して取得した Series を書き換えると、元の dataframe も書き換わる。
- 元の DataFrame を書き換えたくない場合は、.copy() で1列取得する。
インデックス名称指定
# DataFrame から1列指定して取得した Series
srs_column0 = get_sample_df()['column0']
print_data_type('DataFrame から1列指定して取得した Series',srs_column0)
# 1要素取得
print_data_type('1要素取得',srs_column0['index0'])
# 1要素取得(.の後にインデックス名称)
print_data_type('1要素取得(.の後にインデックス名称)',srs_column0.index0)
# リスト指定
print_data_type('リスト指定',srs_column0[['index0','index2']])
# スライス指定
print_data_type('スライス指定',srs_column0['index0':'index2'])
[DataFrame から1列指定して取得した Series]
index0 0
index1 1
index2 2
Name: column0, dtype: int64
<class 'pandas.core.series.Series'>
[1要素取得]
0
<class 'numpy.int64'>
[1要素取得(.の後にインデックス名称)]
0
<class 'numpy.int64'>
[リスト指定]
index0 0
index2 2
Name: column0, dtype: int64
<class 'pandas.core.series.Series'>
[スライス指定]
index0 0
index1 1
index2 2
Name: column0, dtype: int64
<class 'pandas.core.series.Series'>
行番号
# DataFrame から1列指定して取得した Series
srs_column0 = get_sample_df()['column0']
print_data_type('DataFrame から1列指定して取得した Series',srs_column0)
# 1行取得
print_data_type('1行取得',srs_column0[0])
# リスト指定
print_data_type('リスト指定',srs_column0[[0,2]])
# スライス指定
print_data_type('スライス指定',srs_column0[0:3])```
[DataFrame から1列指定して取得した Series]
index0 0
index1 1
index2 2
Name: column0, dtype: int64
<class 'pandas.core.series.Series'>
[1行取得]
0
<class 'numpy.int64'>
[リスト指定]
index0 0
index2 2
Name: column0, dtype: int64
<class 'pandas.core.series.Series'>
[スライス指定]
index0 0
index1 1
index2 2
Name: column0, dtype: int64
<class 'pandas.core.series.Series'>
DataFrame から1列指定して取得した Series を書き換えると、元の dataframe も書き換わる。
df = get_sample_df()
print_data_type('書き換え前',df)
srs_column0 = df['column0']
# 書き換できる
srs_column0['index0'] = 100
srs_column0[1] = 200
srs_column0.index2 = 300
# 元のdataframeも書き換わる
print_data_type('書き換え後',df)
[書き換え前]
column0 column1 column2
index0 0 10 20
index1 1 11 21
index2 2 12 22
<class 'pandas.core.frame.DataFrame'>
[書き換え後]
column0 column1 column2
index0 100 10 20
index1 200 11 21
index2 300 12 22
<class 'pandas.core.frame.DataFrame'>
元の DataFrame を書き換えたくない場合は、.copy() で1列取得する。
# 列指定のコピーで series を取得すると、元の dataframe は書き換わらない
df = get_sample_df()
print_data_type('書き換え前',df)
srs_column0 = df['column0'].copy()
srs_column0['index0'] = 100
srs_column0[1] = 200
print_data_type('書き換え後',srs_column0)
print_data_type('書き換え後',df)
[書き換え前]
column0 column1 column2
index0 0 10 20
index1 1 11 21
index2 2 12 22
<class 'pandas.core.frame.DataFrame'>
[書き換え後]
index0 100
index1 200
index2 2
Name: column0, dtype: int64
<class 'pandas.core.series.Series'>
[書き換え後]
column0 column1 column2
index0 0 10 20
index1 1 11 21
index2 2 12 22
<class 'pandas.core.frame.DataFrame'>
DataFrame から1行指定して取得した DataFrame
- 1行取り出すことはできても、スライス指定なので、DataFrameとして取得される。
- なので、次に1列指定して取り出しても、 Series となるので、そのままでは書き換えができない。
- 元の DataFrame は書き換わらない。
- ワーニングも出るので非推奨。
- 1行の DataFrame を転置しても、 Series にはならない。
- 書き換える場合は、loc/iloc で1行取り出し、Series として扱う。(推奨)
# DataFrame から1行指定して取得した DataFrame
df = get_sample_df()
df_index0 = df['index0':'index0']
print_data_type('DataFrame から1行指定して取得した DataFrame',df_index0)
# 1列取得
print_data_type('1列取得',df_index0['column0'])
[DataFrame から1行指定して取得した DataFrame]
column0 column1 column2
index0 0 10 20
<class 'pandas.core.frame.DataFrame'>
[1列取得]
index0 0
Name: column0, dtype: int64
<class 'pandas.core.series.Series'>
# 元の dataframe は書き換わらない
df_index0['column0'] = 100
print_data_type('書き換え後',df_index0)
print_data_type('書き換え後',df)
[書き換え後]
column0 column1 column2
index0 100 10 20
<class 'pandas.core.frame.DataFrame'>
[書き換え後]
column0 column1 column2
index0 0 10 20
index1 1 11 21
index2 2 12 22
<class 'pandas.core.frame.DataFrame'>
A value is trying to be set on a copy of a slice from a DataFrame.
Try using .loc[row_indexer,col_indexer] = value instead
See the caveats in the documentation: https://pandas.pydata.org/pandas-docs/stable/user_guide/indexing.html#returning-a-view-versus-a-copy
df_index0['column0'] = 100
# 1行 dataframe を転置
df_index0_T = df_index0.T
print_data_type('1行 dataframe を転置',df_index0_T)
[1行 dataframe を転置]
index0
column0 100
column1 10
column2 20
<class 'pandas.core.frame.DataFrame'>
書き換える場合は、loc/iloc で1行取り出し、Series として扱う。
# 書き換える場合は、loc/iloc で1行取り出し、Series として扱う。
# DataFrame からlocで行を取り出したSeries
df = get_sample_df()
df_index0 = df.loc['index0']
print_data_type('書き換え前',df)
print_data_type('書き換え前',df_index0)
print_data_type('DataFrame からlocで行を取り出したSeries',df_index0)
df_index0['column0'] = 100
print_data_type('書き換え後',df_index0)
print_data_type('書き換え後',df)
[書き換え前]
column0 column1 column2
index0 0 10 20
index1 1 11 21
index2 2 12 22
<class 'pandas.core.frame.DataFrame'>
[書き換え前]
column0 0
column1 10
column2 20
Name: index0, dtype: int64
<class 'pandas.core.series.Series'>
[DataFrame からlocで行を取り出したSeries]
column0 0
column1 10
column2 20
Name: index0, dtype: int64
<class 'pandas.core.series.Series'>
[書き換え後]
column0 100
column1 10
column2 20
Name: index0, dtype: int64
<class 'pandas.core.series.Series'>
[書き換え後]
column0 column1 column2
index0 100 10 20
index1 1 11 21
index2 2 12 22
<class 'pandas.core.frame.DataFrame'>
ループ
- DataFrame をそのままループしても、カラム名しか取得できない。
- 列方向にループするには、items()
- Series として取得できるので、行名、行番号指定で書き換え可能
- 行方向にループするには、iterrows()
- Series として取得できるので、列名、列番号指定で書き換え可能
DataFrame をそのままループ
長くなるので、1回でbreakする
df = get_sample_df()
for column in df:
print_data_type('column',column)
break
[column]
column0
<class 'str'>
列方向にループ
まず、列方向、行方向にどのようなデータが格納されているかすぐに確認できるように、データフレームの中身を再表示しておく。
print_data_type('SampleDataFrame',get_sample_df())
[SampleDataFrame]
column0 column1 column2
index0 0 10 20
index1 1 11 21
index2 2 12 22
<class 'pandas.core.frame.DataFrame'>
df = get_sample_df()
# items で、列方向にループ
for column, data in df.items():
print_data_type('column',column)
print_data_type('data',data)
data['index0'] = 100
data[1] = 200
data.index2 = 300
break
print_data_type('書き換え後',df)
[column]
column0
<class 'str'>
[data]
index0 0
index1 1
index2 2
Name: column0, dtype: int64
<class 'pandas.core.series.Series'>
[書き換え後]
column0 column1 column2
index0 100 10 20
index1 200 11 21
index2 300 12 22
<class 'pandas.core.frame.DataFrame'>
行方向にループ
df = get_sample_df()
# iterrows で、行をループ
for index, data in df.iterrows():
print_data_type('index',index)
print_data_type('data',data)
data['column0'] = 100
data[1] = 200
data.column2 = 300
break
print_data_type('書き換え後',df)
[index]
index0
<class 'str'>
[data]
column0 0
column1 10
column2 20
Name: index0, dtype: int64
<class 'pandas.core.series.Series'>
[書き換え後]
column0 column1 column2
index0 100 200 300
index1 1 11 21
index2 2 12 22
<class 'pandas.core.frame.DataFrame'>
まとめ
データフレームからの列・行の取り出し方について一通りまとめみたが、難しいことはまったくなかった。基本的なことなので、都度ググることなく、スラスラと書いていけるようにしていきたい。