0
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

はじめてのアドベントカレンダーAdvent Calendar 2023

Day 13

【初学者向け】データフレームの取扱説明書

Posted at

目的

データフレームからの列・行の取り出し方について一通りまとめる。

背景

データを読み込んでデータフレームを作って、特定の行や列を参照したり、書き換えしたりしている。困ったときには、都度ググって対処しているが、基本的な使い方を一通り覚えたく、まとめてみた。
私がよく困ったのは、データフレームからデータを取り出したいのに、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'>

まとめ

データフレームからの列・行の取り出し方について一通りまとめみたが、難しいことはまったくなかった。基本的なことなので、都度ググることなく、スラスラと書いていけるようにしていきたい。

0
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?