検索
連載

[pandas超入門]DataFrameに対して行や列を追加したり削除したりしてみようPythonデータ処理入門

pandasには、assignメソッドやinsertメソッド、concat関数、dropメソッドなど、DataFrameオブジェクトに対して、行や列を追加したり削除したりする方法が用意されています。それらの基本的な使い方を見ていきましょう。

PC用表示 関連情報
Share
Tweet
LINE
Hatena
「Pythonデータ処理入門」のインデックス

連載目次

本シリーズと本連載について

 本シリーズ「Pythonデータ処理入門」は、Pythonの基礎をマスターした人を対象に以下のような、Pythonを使ってデータを処理しようというときに便利に使えるツールやライブラリ、フレームワークの使い方の基礎を説明するものです。

  • NumPy(「NumPy超入門」の目次はこちら
  • pandas(本連載)
  • Matplotlib

 なお、本連載では以下のバージョンを使用しています。

  • Python 3.12
  • pandas 2.2.1

 第5回ではat属性やiat属性による要素選択の方法、ブーリアンインデクシングなどについて解説しました。今回はDataFrameオブジェクトに行や列を追加したり、DataFrameオブジェクトから行や列を削除したり、行や列を入れ替えたり、ソートしたりする方法を紹介します。

DataFrameオブジェクトに列や行を追加する

 DataFrameオブジェクトは決まった数の行、決まった数の列から構成される2次元のデータ構造ですが、そこに新しく行または列を追加できます。

DataFrameオブジェクトに列を追加する

 列を追加するには以下の方法があります。

 assignメソッドはDataFrameオブジェクトの最後列に新たに列を追加するもので、insertメソッドは(名前からも分かるように)任意の位置に列を挿入するものです。

 まずは一番簡単な新規の列名を指定して代入する方法から見てみましょう。

新規列名を指定して代入

 DataFrameオブジェクトにブラケット「[]」を付加して、そこに列名(列ラベル)を指定すると対応する列を取得(選択)できます。これを使って、未使用の列を指定して代入することで新しい列をDataFrameオブジェクトに追加できます。ここでは以下に示すコードで作成したDataFrameオブジェクトを例とします。

d = {'col0': ['foo', 'baar', 'baaaz'], 'col1': [10, 30, 50]}
df = pd.DataFrame(d)
print(df)
# 出力結果:
#    col0  col1
#0    foo    10
#1   baar    30
#2  baaaz    50

サンプルとして使用するDataFrameオブジェクト

 このDataFrameオブジェクトに'col2'列を追加するには次のようにします。

df['col2'] = pd.Series([20, 40, 60])  # 存在しない列の列ラベルを指定
print(df)
# 出力結果:
#    col0  col1  col2
#0    foo    10    20
#1   baar    30    40
#2  baaaz    50    60

新規列名を指定して代入(列の追加)

 存在しない列のラベルをブラケット内に指定して代入することで、その列が新規に作成されました。ただし、既に存在している列を指定して代入すると(もちろん)その列の値が上書きされてしまうことは覚えておいてください。

df['col2'] = [200, 400, 600# 既存の列名を指定すると、その列の値が上書きされる
print(df)
# 出力結果:
#    col0  col1  col2
#0    foo    10   200
#1   baar    30   400
#2  baaaz    50   600

既存の列を指定すると、その列の要素の値が上書きされる

 次にassignメソッドから見てみましょう。また、DataFrameオブジェクトの列には今見たようにSeriesオブジェクトやリスト、DataFrameオブジェクトなどを代入可能です。ただし、その要素数は代入先のDataFrameオブジェクトの行数と同じである必要があります。

assignメソッドを使って列を追加する

pandas.DataFrame.assignメソッド

pandas.DataFrame.assign(**kwargs)

pandas.DataFrame.assignメソッドの構文

 呼び出しに使用したDataFrameオブジェクトの最後列に新たに列を追加した、新しいDataFrameオブジェクトを返す。パラメーターは以下の通り。

  • **kwargs:キーワード引数。「キーワード=値」とすると、キーワードが列名(列インデックス)に、その値がその列に含まれる各要素の値になる。値にはラムダ式など呼び出し可能なオブジェクトを指定してもよい。その場合は、対応する要素を基に計算が行われ、その結果が追加される列の値になる

 ここでは以下のようなDataFrameオブジェクトを作成したものとします。

d = {'col0': ['foo', 'baar', 'baaaz'], 'col1': [10, 30, 50]}
df0 = pd.DataFrame(d)
print(df0)
# 出力結果:
#    col0  col1
#0    foo    10
#1   baar    30
#2  baaaz    50

サンプルのDataFrameオブジェクト

 以下は、このDataFrameオブジェクトに'col2'列を追加するコードの例です。元のDataFrameオブジェクト(df0)に対してassignメソッドを呼び出した結果をdf1オブジェクトに代入して、その値を表示しています。また、その次の「print(df0)」行の出力結果にも注目してください。

df1 = df0.assign(col2=[20, 40, 60])
print(df1)
# 出力結果:
#    col0  col1  col2
#0    foo    10    20
#1   baar    30    40
#2  baaaz    50    60

print(df0)
# 出力結果:
#    col0  col1
#0    foo    10
#1   baar    30
#2  baaaz    50

DataFrameオブジェクトに'col2'列を追加

 この例では、assignメソッドのキーワード引数として「col2=[20, 40, 60]」と指定しています。この結果、キーワード「col2」が追加される列の名前(列インデックス)となり、リストに含まれる値がその列の要素の値となっています。なお、指定した値の要素数は先ほどの新規列名を指定しての代入と同様、元のDataFrameオブジェクトの行数に等しくなければいけません。この場合は3行なので、値の要素数もちょうど3つになるように指定する必要があります。また、上の例で示しているようにDataFrameオブジェクトではなくリストやSeriesオブジェクトを新しい列の要素の値として指定できるのも、先ほどの代入による列の追加と同様です。

 そして、2つ目の出力結果にあるように、assignメソッドは元のDataFrameオブジェクトの内容を変更しないことも覚えておきましょう。

 キーワード引数にはラムダ式など呼び出し可能なオブジェクトを指定することも可能です。そこで、'col0'列の各要素の文字数を'col3'列の要素として追加してみることにします。

df2 = df1.assign(col3=lambda x: len(x['col0']))
print(df2)
# 出力結果:
#    col0  col1  col2  col3
#0    foo    10    20     3
#1   baar    30    40     3
#2  baaaz    50    60     3

想定外の結果

 'col0'列の要素の文字数は順に、3文字、4文字、5文字なので'col3'列の値もそうなりそうなものですが(筆者も最初は思いました)、そうはなっていません。実はラムダ式のパラメーター(ここではx)には恐らくDataFrameオブジェクトそのものが渡されるので、これは'col0'列の要素数(行数)をlen関数で計算しているだけなのです。

 そうではなく、テキストの文字数を得るにはSeriesオブジェクトが持つstr属性が提供している各種メソッドを使う必要があります(ここでは詳しくは説明しません)。簡単には、「df['col0']」のようにして列名を指定すると得られるのはSeriesオブジェクトです。そして、そのstr属性が提供するlenメソッドを呼び出すことで、各要素の文字数が得られます。

print(df1['col0'].str.len())
# 出力結果:
#0    3
#1    4
#2    5
#Name: col0, dtype: int64

'col0'列の各要素の文字数を計算

 このことを使って以下のようなコードを書くと、文字数を各要素の値とする列を追加できます。なお、列にアクセスするには「x['col0']」のような書き方に加えて「x.col0」のような書き方も可能です。以下ではその書き方を使っています。

df2 = df1.assign(col3=lambda x: x.col0.str.len())
print(df2)
# 出力結果:
#    col0  col1  col2  col3
#0    foo    10    20     3
#1   baar    30    40     4
#2  baaaz    50    60     5

'col0'列の文字数を値とする'col3'列が追加できた

 最後にキーワード引数は複数指定できます。このときには、キーワード引数として後に記述したものが後ろに追加されていきます。

df3 = df2.assign(col4=lambda x: x.col0.str[0], col5=lambda x: x.col0.str.upper())
print(df3)
# 出力結果:
#    col0  col1  col2  col3 col4   col5
#0    foo    10    20     3    f    FOO
#1   baar    30    40     3    b   BAAR
#2  baaaz    50    60     3    b  BAAAZ

キーワード引数を複数指定する例

 キーワード引数に記述した順番にDataFrameオブジェクトに列が追加されていることにまずは注目してください。また、「col4=lambda x: x.col0.str[0]」としているので'col4'列の値はstr属性の先頭文字に、また「col5=lambda x: x.col0.str.upper()」としているので'col5'列の値は'col0'列の値を大文字化したものになっているのが分かります。

 次にinsertメソッドを使う方法です。

insertメソッドを使って列を追加する

pandas.DataFrame.insertメソッド

pandas.DataFrame.insert(loc, column, value,
                        allow_duplicates=_NoDefault.no_default)

pandas.DataFrame.insertメソッドの構文

 locパラメーターで指定した位置に、columnパラメーターで指定した名前の列を挿入した新しいDataFrameオブジェクトを返す(新しい列の値はvaluesパラメーターで指定されたものになる)。パラメーターは以下の通り。

  • loc:新規列を挿入する箇所を整数値で指定する
  • column:新規列の列名(列インデックス)
  • value:新規列の値
  • allow_duplicates:同じ名前の列が存在することを許すかどうかを指定。省略した場合は許さない。

 以下に例を示します。ここでもこれまでと同じく、次のようなDataFrameオブジェクトがあるものとします。

d = {'col0': ['foo', 'baar', 'baaaz'], 'col1': [10, 30, 50]}
df = pd.DataFrame(d)

サンプルのDataFrameオブジェクト

 以下は、このDataFrameオブジェクトの第1列へ新規に列を挿入する例です。

df.insert(1, 'new_column', [1, 3, 5])
print(df)
# 出力結果:
#    col0  new_column  col1
#0    foo           1    10
#1   baar           3    30
#2  baaaz           5    50

第1列へ新規に列を挿入

 「df.insert(1, 'new_column', [1, 3, 5])」としているので、挿入箇所は第1列、その列名(列ラベル)は「new_column」、値は1、3、5です。実際にそうした列が挿入されていますね。assignメソッドとは異なり、元のDataFrameオブジェクトへ列が挿入されている点には注意してください。

 既存の列と同じ名前の列を挿入しようとすると、通常はValueError例外が発生します。

df.insert(3, 'col1', [20, 40, 60])  # ValueError

'col1'というラベルを持つ列は既にあるのでValueError例外が発生する

 ただし、allow_duplicatesパラメーターにTrueを指定すると、同じ列ラベルが重複するのが許され、例外が発生しなくなります。

df.insert(3, 'col1', [20, 40, 60], allow_duplicates=True# OK
print(df)
# 出力結果:
#    col0  new_column  col1  col1
#0    foo           1    10    20
#1   baar           3    30    40
#2  baaaz           5    50    60

allow_duplicatesパラメーターにTrueを指定すると、同じ名前の列が存在することが許されるようになる

 いずれにせよ、既存の列の値が上書きされるような挙動とはならない(例外が発生するか、既存の列とは別の列が作成されるか)ことには注意しましょう。

 DataFrameオブジェクトに列を挿入するには、pandasのconcat関数も使えます。が、これについては、DataFrameオブジェクトに行を挿入する方法を見た後で紹介します。

DataFrameオブジェクトに行を追加する

 DataFrameオブジェクトに行を追加するにはloc属性の第1引数に行インデックスとして「存在しない」行インデックスを指定して代入します。ここでは以下のように行インデックスが1から始まるDataFrameオブジェクトを作成したものとしましょう。

kawasaki = {'name': 'kawasaki', 'age': 60, 'height': 170}
isshiki = {'name': 'isshiki', 'age': 45, 'height': 168}
endo = {'name': 'endo', 'age': 55, 'height': 175}

df = pd.DataFrame([kawasaki, isshiki, endo], index=[1, 2, 3])
print(df)
# 出力結果:
#       name  age  height
#1  kawasaki   60     170
#2   isshiki   45     168
#3      endo   55     175

サンプルのDataFrameオブジェクト

 このDataFrameオブジェクトの末尾に新たに行を追加するには以下のようにします。

df.loc[4] = ['shimada', 58, 180]
print(df)
# 出力結果:
#       name  age  height
#1  kawasaki   60     170
#2   isshiki   45     168
#3      endo   55     175
#4   shimada   58     180

行インデックス「4」に行を追加

 行インデックスがどのように設定されているかに応じて、どんな行インデックスを指定すればよいかが変わってくることには注意してください。この例では行インデックスが1始まりの連番なので、行を追加するつもりでloc属性の行インデックスとして安直に「len(df)」を指定すると既存行が上書きされてしまいます。

df.loc[len(df)] = ['ogawa', 60, 175]
print(df)
# 出力結果:
#       name  age  height
#1  kawasaki   60     170
#2   isshiki   45     168
#3      endo   55     175
#4     ogawa   60     175

末尾行が上書きされてしまった

 また、(loc属性ではなく)iloc属性の第1引数に末尾行を意味する「-1」を指定しても同様な結果になります。というわけで、行を新規に追加するのであれば、次に紹介するpandas.concat関数を使うのがよいでしょう。

 なお、以前はDataFrameオブジェクトにappendメソッドがあり、これを使って、行をDataFrameオブジェクトに追加できたのですが、現在ではこのメソッドは削除されています(_appendメソッドが代わりにありますが、名前が変わったことからも分かるように、使用を推奨はされていません)。

DataFrameオブジェクトに列や行を追加する

 DataFrameオブジェクトに列または行を追加するための汎用(はんよう)的な関数がpandas.concat関数です。

pandas.concat関数

pandas.concat(objs, *, axis=0, ignore_index=False, sort=False, copy=None)

pandas.concat関数の構文(一部)

 pandasがサポートするオブジェクトをaxisパラメーターで指定された方向に結合する。以下に幾つかのパラメーターとその説明を示す。全てのパラメーターについてはpandasのドキュメント「pandas.concat」を参照のこと。

  • objs:結合対象のオブジェクトをシーケンスオブジェクト(リストなど)またはマッピングオブジェクト(辞書など)として渡す。省略不可
  • axis:結合する方向。0を指定すると行方向に、1を指定すると列方向に結合する。省略時には0が指定されたものと見なされる
  • ignore_index:結合時にインデックス(ラベル)を無視するかどうかを指定する。Trueなら結合対象のオブジェクトの結合方向の軸ラベルを使用しない。省略時はFalseが指定されたものと見なされる(軸ラベルを使用する)
  • sort:結合する軸方向ではない軸に沿って要素をソートするかどうかを指定する。Trueならソートし、Falseならソートしない。省略時はFalseが指定されたものと見なされる
  • copy:データをコピーするかどうかを指定する。Trueならコピーし、Falseなら必要のないコピーはしない。省略時にはTrueが指定されたものと見なされる

 ここでは以下の2つのDataFrameオブジェクトを作成しておきます。

df0 = pd.DataFrame({'col0': ['foo', 'baar', 'baaaz'],
                    'col1': [10, 30, 50],
                    'col2': [20, 40, 60]},
                    index=[1, 2, 3])
print(df0)
# 出力結果:
#    col0  col1  col2
#1    foo    10    20
#2   baar    30    40
#3  baaaz    50    60

df1 = pd.DataFrame([['qux', 70, 80]],
                   columns=['col0', 'col1', 'col2'], index=[4])
print(df1)
# 出力結果:
#  col0  col1  col2
#4  qux    70    80

サンプルのDataFrameオブジェクト

 ここでpandas.concat関数を呼び出して、2つのDataFrameオブジェクトを結合してみましょう。

df2 = pd.concat([df0, df1])  # 行方向に結合
print(df2)
# 出力結果:
#    col0  col1  col2
#1    foo    10    20
#2   baar    30    40
#3  baaaz    50    60
#4    qux    70    80

df2 = pd.concat([df0, df1], axis=0# 上と同じ
print(df2)  # 出力結果:上と同じ

行方向に結合

 最初のconcat関数呼び出しではaxisパラメーターの指定がありません。この場合はデフォルトで行方向(縦方向)にDataFrameオブジェクトが結合されます。つまり、DataFrameオブジェクトに行を追加していると考えられます。loc属性を使うよりも、こちらの方が安全に行を追加できるでしょう。

 なお、これは2つ目の「axis=0」を指定したconcat関数呼び出しと同じことを意味している点にも注意してください。このとき、ignore_indexパラメーターにTrueを指定すると、結合する軸に沿ったラベルが振り直されます。

df2 = pd.concat([df0, df1], axis=0, ignore_index=True)
print(df2)
# 出力結果:
#    col0  col1  col2
#0    foo    10    20
#1   baar    30    40
#2  baaaz    50    60
#3    qux    70    80

ignore_indexパラメーターにTrueを指定

 先ほどは行方向の軸ラベル(インデックス)が1〜4だったのに、今度は0〜3になっていることを確認してください。独自の基準で軸ラベルを付与しているDataFrameオブジェクト同士を結合すると、軸ラベルが意味を成さなくなることがあります。そうした場合には「ignore_index=True」を指定することで軸ラベルを振り直せます。

 では、「axis=1」を指定して呼び出すとどうなるでしょう。これは列を追加することに相当します。

df2 = pd.concat([df0, df1], axis=1# 列方向に結合
print(df2)
# 出力結果:
#    col0  col1  col2 col0  col1  col2
#1    foo  10.0  20.0  NaN   NaN   NaN
#2   baar  30.0  40.0  NaN   NaN   NaN
#3  baaaz  50.0  60.0  NaN   NaN   NaN
#4    NaN   NaN   NaN  qux  70.0  80.0

列方向に結合してみた

 注意してほしいのは、これはあくまでも「列方向に結合してみた」だけで意味のある行為ではない点です。実際、列ラベルが重複していることからも、この結果には意味を見出せないことが想像できます。concat関数を呼び出すだけで複数のDataFrameオブジェクトやSeriesオブジェクトを結合できますが、その結果を意味あるものにするのはコードを書く人の責任であることは心にとどめておきましょう。

 ここまではDataFrameオブジェクトの結合について見てきましたが、pandas.concat関数ではSeriesオブジェクト同士を結合したり、またはDataFrameオブジェクトとSeriesオブジェクトを結合したりすることも可能です。

 簡単に例を示しておきましょう。

s0 = pd.Series([10, 20], name='col0')
s1 = pd.Series([30, 40], name='col1')

s = pd.concat([s0, s1])  # Seriesオブジェクトを行方向に結合
print(s)
# 出力結果:
#0    10
#1    20
#0    30
#1    40
#dtype: int64

df = pd.concat([s0, s1], axis=1# Seriesオブジェクトを列方向に結合
print(df)
# 出力結果:
#   col0  col1
#0    10    30
#1    20    40

s2 = pd.Series([50, 60], name='col2')
df = pd.concat([df, s2], axis=1# DataFrameオブジェクトとSeriesオブジェクトを列方向に結合
print(df)
# 出力結果:
#   col0  col1  col2
#0    10    30    50
#1    20    40    60

Seriesオブジェクト同士の結合、DataFrameオブジェクトとSeriesオブジェクトの結合

 最初の例ではSeriesオブジェクト同士を行方向に結合しています。その結果、Seriesオブジェクトのname属性('col0'と'col1')は無視されて、インデックスも0と1が繰り返されています。

 2つ目の例ではSeriesオブジェクト同士を列方向に結合しています。このときには得られるDataFrameオブジェクトの列ラベルとしてname属性が使われていることが分かります。

 最後の例ではDataFrameオブジェクトとSeriesオブジェクトが列方向に結合されています。ここでは、SeriesオブジェクトがDataFrameオブジェクトに新しい列として追加されています(name属性の値も列ラベルとして使われていることが分かります)。

DataFrameオブジェクトから行/列を削除する

 最後にDataFrameオブジェクトから指定した行や列を削除する方法も紹介しておきましょう。これにはdropメソッドを使います。

pandas.DataFrame.dropメソッド

pandas.DataFrame.drop(labels=None, *, axis=0,
                      index=None, columns=None, inplace=False)

pandas.DataFrame.dropメソッドの構文(一部)

 labelsパラメーターとaxisパラメーターで指定した行または列を、もしくはindexパラメーターで指定した行またはcolumnsパラメーターで指定した列を削除する。一部のパラメーターの意味は次の通り。全てのパラメーターについてはpandasのドキュメント「pandas.DataFrame.drop」を参照のこと。

  • labels:削除する軸ラベルを指定する。複数のラベルを指定するにはリストを使用する
  • axis:0ならインデックス(行方向の軸ラベル)を削除し、1なら列ラベルを削除することを意味する。省略時には0が指定されたものと見なされる
  • index:削除するインデックスを指定する。labelsパラメーターに行インデックスを指定して、axisパラメーターに0を指定したのと同等
  • columns:削除する列インデックスを指定する。labelsパラメーターに列インデックスを指定して、axisパラメーターに1を指定したのと同等
  • inplace:削除をインプレースで行うかどうかの指定。Trueなら呼び出しに使用したDataFrameオブジェクトから行または列が削除される(戻り値はない)。Falseなら行または列を削除した新たなオブジェクトが返送される

 ここでは以下のDataFrameオブジェクトを作成して、試してみましょう。

kawasaki = {'name': 'kawasaki', 'age': 60, 'height': 170}
isshiki = {'name': 'isshiki', 'age': 45, 'height': 168}
endo = {'name': 'endo', 'age': 55, 'height': 175}

df = pd.DataFrame([kawasaki, isshiki, endo], index=['row0', 'row1', 'row2'])
print(df)
# 出力結果:
#          name  age  height
#row0  kawasaki   60     170
#row1   isshiki   45     168
#row2      endo   55     175

サンプルとして使用するDataFrameオブジェクト

 ここから'row1'行を削除するには次のようなコードを書きます。

df2 = df.drop(labels='row1', axis=0)
print(df2)
# 出力結果:
#          name  age  height
#row0  kawasaki   60     170
#row2      endo   55     175

df2 = df.drop(index='row1')
print(df2)  # 出力結果:上の例と同じ

print(df)
# 出力結果:
#          name  age  height
#row0  kawasaki   60     170
#row1   isshiki   45     168
#row2      endo   55     175

行の削除

 最初の例ではlabelsパラメーターに'row1'を指定して、axisパラメーターに0を指定しているので'row1'行が削除されます。このとき、labelsパラメーターにはインデックス(行ラベル)を指定したのに、axisパラメーターには1を指定するとKeyError例外が発生します(列ラベルにも同じものがない限り)。整合性のある指定が必要なことには注意してください。

 次の例ではこれら2つのパラメーターを指定する代わりにindexパラメーターに'row1'を指定する例です。結果は最初の例と同様になります。

 これら2つの例ではinplaceパラメーターを指定していないので、ここでは行を削除した結果となる新しいDataFrameオブジェクトが返送されていることにも注意してください。そのため、元のDataFrameオブジェクトである「df」の内容は変更されていません(最後の出力)。

 次に列を削除する例です。

df2 = df.drop(labels='age', axis=1)
print(df2)
# 出力結果:
#          name  height
#row0  kawasaki     170
#row1   isshiki     168
#row2      endo     175

df2 = df.drop(columns='age')
print(df2)  # 出力結果:上の例と同じ

列の削除

 先ほどのコードと同様ですが、削除する列の列ラベルをlabelsパラメーターやcolumnsパラメーターに指定する点が異なります。

 複数の行や列を削除するにはlabels/index/columnsパラメーターに削除対象のラベルを要素とするリストを指定します。

df2 = df.drop(labels=['age', 'height'], axis=1)
print(df2)
# 出力結果:
#          name
#row0  kawasaki
#row1   isshiki
#row2      endo

df2 = df.drop(index=['row1', 'row2'])
print(df2)
# 出力結果:
#          name  age  height
#row0  kawasaki   60     170

複数の行や列の削除

 最後にインプレースで削除してみましょう。

df.drop(labels='age', axis=1, inplace=True)
print(df)
# 出力結果:
#          name  height
#row0  kawasaki     170
#row1   isshiki     168
#row2      endo     175

インプレースでの削除


 今回はDataFrameオブジェクトに対して行や列を追加したり、削除したりする方法を紹介しました。次回は行や列の順序の入れ替えやソートなどの方法について見ていく予定です。

「Pythonデータ処理入門」のインデックス

Pythonデータ処理入門

Copyright© Digital Advantage Corp. All Rights Reserved.

[an error occurred while processing this directive]
ページトップに戻る