見出し画像

第3回 DataFrameを連結するconcat

concat関数

前回の記事では、様々なIndexクラスがあり、Indexクラスによる一部の恩恵を紹介しました。しかし、Indexクラスの本当のメリットはこのconcat関数にあります。

concat関数は、データ行/列を連結する関数としてよく説明されます。確かにその通りですが、正確な理解をするには少し説明不足です。concat関数とは、データ行または列のIndexオブジェクトの値を元に、Series/DataFrameを連結する関数と理解すべきです。

下記のコードは、代表的なconcat関数の利用例のコードです。concat関数の具体的な処理のイメージをつかみましょう。

スクリーンショット 2019-11-17 16.34.02

上記のconcat関数は、DataFrameであるdf1とdf2を、columnsのIndexオブジェクトの値に基づいてデータ列を結合し、データ行を連結しています。データ行は連結されるだけなので、同じIndex値があったとしても、別のデータ行として処理されます。df1とdf2のcolumnsのIndexオブジェクトの両方に存在している'd'は、全データ行に値が格納されています。一方、df1とdf2のcolumnsのIndexオブジェクトの片方にしか存在しないデータ列は、該当のデータがない場合は空となります。

concat関数は、多くのオプション引数を持っています。下記の関数の紹介で、オプション引数の概要を説明しています。

// 複数のDataFrameまたはSeriesを連結する。

// 引数のaxisは、連結方向を指定します。
//  axis=0とすると、列のIndex値に基づき列をマージし、行を連結をします。
//  axis=1とすると、行のIndex値に基づき行をマージし、列を連結をします。

// 引数のsortは、連結した行名/列名の並び替えの実施有無を設定します。

// 引数のjoinは、連結方法を設定します。
//  joinにouterを指定すると、連結するDataFrameいずれかに存在するIndex値のみ残して連結させます。
//  joinにinnerを指定すると、連結するDataFrame全てに存在するIndex値のみ残して連結させます。

// 引数のignore_indexは、連結したIndex値の破棄の実施有無を設定します。
//  ignore_indexをTrueにすると、連結した元のIndexを破棄して、RangeIndexに置換します。

// 引数のkeysを設定すると、連結した行/列のIndexをkeysの値を元にMultiIndexとします。
// 引数にlevelsを設定すると、keysを用いて生成したMultiIndexのlevelsを設定できます。

// 引数のverify_integrityをTrueにすると、連結したIndexの値に重複がないかチェックを行います。

pd.concat([df1, df2, series1 ...], axis=連結する方向, sort=並び替えの実施有無,
          join=連結方法, ignore_index=連結したIndexの破棄の実施有無,
          keys=MultiIndexに新たに追加する値のリスト,
          levels=MultiIndexに新たに追加する値のlevels,
          verify_integrity=連結したIndexの値の重複チェックの実施有無)

concat関数:axisオプション引数

それでは、実際に引数による動作の違いをサンプルコードをみて学んでいきましょう。まずは、axisを1にした時について、例示します。

スクリーンショット 2019-11-18 13.36.44

先ほどとは異なり、上記のconcat関数は、DataFrameであるdf1とdf2を、indexのIndexオブジェクトの値に基づいてデータ行を結合し、データ列を連結しています。当然、データ列に同じIndex値があったとしても、別のデータ列として処理されています。

concat関数:sortオプション引数

次にsortをTrueにした時について、例示します。

スクリーンショット 2019-11-18 13.44.55

sortは、結合したデータ行/列の並び替えの実行有無を設定するオプション引数です。連結したデータ行/列の並び替えは行いません。

大量データを日々扱っている方は、重々承知だと思いますが、大量データのsort処理は非常に重い処理です。一方、concat関数におけるsort処理が必要となるケースは多くはないので、並び替えが必須でなく場合は、必ずsortをFalseに設定しましょう。(感覚的には1000万を越える影響が徐々にでてくるイメージです。)ただし、join='inner'となっている場合は、内部でsort処理が行われているので、出力は変わりますが、処理の重さには影響がありません。

現状のconcat関数は、デフォルトsort=Trueとなっていますが、今後はデフォルトsort=Falseに変更するとアナウンスしています。sort引数を明示的に指定していない場合には、警告メッセージが表示されるので、そのためにもsort引数は指定するようにしましょう。

concat関数:joinオプション引数

それでは、次にjoinを'inner'にした時について、例示します。

スクリーンショット 2019-12-15 10.57.46

joinに'inner'を指定した場合は、連結するDataFrame全てに存在するデータ行/列のみを残して、DataFrameを連結します。

concat関数:ignore_indexオプション引数

それでは、次にignore_indexをTrueにした時について、例示します。

スクリーンショット 2019-12-15 11.25.40

ignore_indexは、連結したデータ行/列名の元の値を破棄して、RangeIndexをに変更する(つまりreset_index関数をした状態にする)かどうかを指定するオプション引数です。元のデータ行/列名が必要ない場合は、指定しましょう。

concat関数:keys, levelsオプション引数

次にkeysに値リストを設定した時について、例示します。

スクリーンショット 2019-12-15 13.52.39

keysを指定すると、結合するデータ行/列を指定した値リストを上位levelのKeyとしたMultiIndexに変換してから連結します。そのため、例え元が同じ行/列名であっても連結後は、別の行/列として存在します。

また、下のコードのように、生成したMulitiIndexのlevelsを、levels引数によって指定することもできます。levelsとは、MultiIndexのマスタデータみたいなものです。MultiIndexについては、次回の第4回で詳しく説明します。またその際には、MultiIndexを持つDataFrame同士のconcat関数による連結も合わせて紹介します。

スクリーンショット 2019-12-15 13.57.12

concat関数:verify_integrityオプション引数

最後に、verify_integrity=Trueにした時について、例示します。

スクリーンショット 2019-12-15 13.57.29

verify_integrityは、連結した行/列名に重複した値のチェック有無を設定するオプション引数です。重複した値がある場合、ValueErrorをraiseしてくれます。特に開発時などに意図しない重複が発生しないかチェックするのに便利です。

concat関数:3つ以上のDataFrameの連結

concat関数で連結する対象は3つ以上でも連結することができます。下記がそのコード例です。内包表記などで返ってきたDataFrameリストをconcat関数に渡して、1つのDataFrameにして返すといった書き方が実現でき、便利です。

スクリーンショット 2019-12-16 16.49.28

concat関数:Seriesオブジェクトを混ぜた連結

concat関数は、DataFrameオブジェクト以外にもSeriesオブジェクトも追加することができます。それでは、DataFrameオブジェクトとSeriesオブジェクトを合わせたリストをconcat関数でどうなるのでしょうか?まずは、データ列を結合してデータ行を連結します(axis=0)。下記はコード例です。

スクリーンショット 2019-12-16 16.54.10

上記のように、Seriesにnameが設定されていても、列名としては扱われず、列名が0とされてしまい、データ列は結合されていません。一方、行名はDataFrameと同様に扱われ、連結後のDataFrameに反映されています。Seriesのnameを列名として反映させたい場合は、to_frame関数を利用して、一度DataFrameに変換してから連結する必要があります。下記は、コード例です。

スクリーンショット 2019-12-16 16.54.10 2

次に、DataFrameオブジェクトとSeriesオブジェクトを合わせたリストを、concat関数で、データ行を結合してデータ列を連結する場合(axis=1)について説明してます。下記は、コード例です。

スクリーンショット 2019-12-16 16.54.20

上記のように、この場合はSeriesオブジェクトのindex値は行名、nameは列名として扱われ、連結されます。(不思議な仕様ですが、axisによって処理が大きく異なることは、ライブラリの中でaxis値によって分岐処理をしていることが多いためか、pandasではよくあります。Seriesオブジェクトを混ぜる時は必ずto_frame関数を使うなど、間違えないように気をつけましょう。)

concat関数:left join, right joinの実現方法

concat関数を利用して、連結する片方のDataFrameに存在するデータ行/列のみを残したい時があると思います。しかし、concat関数は'outer'と'inner'しか準備されておらず、'left join'や'right join'はありません。ですが、reindex関数を利用し、事前に残したいデータ行/列に合わせておけば、簡単に実現することができます。下記は、コード例です。(ちなみに、公式ドキュメントでも、下記のようなコードを利用してくれと紹介されています。)

スクリーンショット 2019-12-16 16.59.51

活用事例:複数のcsvファイルの連結

1日毎に同じデータ列をもつcsvファイルがある時など、複数のcsvファイルを1つのDataFrameとして読み取りたい時があると思います。その時、下記のようにconcat関数を利用すればすっきりと書けます。

スクリーンショット 2019-12-22 14.06.32

活用事例:データ値内のリストをカラム名に変換

データ行毎の処理によって、異なるデータ列が生成され、それらを最後に連結したいケースがあります。例えば、単語列からBag of Wordsに変換する処理などです。(最初からスパースマトリックスとして生成する方が良いという話はありますが・・・)

実は、私も一部翻訳に参加したオライリー社の"機械学習のための特徴量エンジニアリング-その原理とPythonによる実践-"において単語列からBag of Wordsに変換する処理があったのですが、原著のサンプルコードではconcat関数の活用をせず、非常に複雑で非効率なコードで書かれていました。このようにある程度Pandasに慣れている人でもconcat関数の活用を気づかないで非効率なコードを書いてしまうことは少なくありません。

concat関数の有効活用を気づくためには、columnsやindexをデータを格納できる場所として意識することが重要です。下記には、単語列からBag of Wordsに変換する処理を例示しています。

スクリーンショット 2019-12-22 14.06.43

まとめ

今回は、concat関数の紹介でした。個人的には、使い方次第では非常に有用な関数だと思っています。一方で、あまり仕様が広く認識されておらず、活用されていることが少ないので、ぜひこれを機会に使ってみてください。

この記事が気に入ったらサポートをしてみませんか?