19
21

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

pandas.DataFrameの作り方あれこれ

この記事では、pandasのDataFrameの作り方について説明します。
Python3.7以上pandas v1.3.5を想定していますが、pandasのバージョンについてはその限りではありません。

有用な使い方は説明できていないので、いい使い方があれば教えていただけると嬉しいです。

結論

長くなってしまったので、最初に結論を書きます。

リストのリスト辞書のリストだけでなく、dataclassのリストnamedtupleのリストからでもDataFrameを簡単に作れます。
リストや辞書だけでなく、dataclassやnamedtupleも用途に応じて使っていきましょう。

import pandas as pd
from dataclasses import dataclass
from collections import namedtuple

# リストのリストの場合
# l = [[1, 2], [3, 4]]
# pd.DataFrame(l, columns=["x", "y"])

# 辞書のリストの場合
# l = [{"x": 1, "y": 2}, {"x": 3, "y": 4}]
# pd.DataFrame(l)

# dataclassの場合
@dataclass
class Point:
  x: int
  y: int

# namedtupleの場合
# Point = namedtuple("Point", ["x", "y"])

p1 = Point(1, 2)
p2 = Point(3, 4)
l = [p1, p2]
pd.DataFrame(l)
x y
0 1 2
1 3 4

pandasのDataFrameを使うのはデータを対話的に分析したいときでしょうから、クラスまで持ち出す必要があるかと言うと微妙です。
複数人で実験をするときや、一人でいろいろな実験を繰り返しするときのコードの可読性やメンテナンス性向上が主なメリットでしょうか。

pandas.DataFrameとは

皆さんpandas使ってますか、DataFrame使ってますか。
ご存じの通り、DataFrameは、2次元の表形式データを表すpandasの主要なデータ構造の一つです。
CSVファイルや、RDBMS(Relational Database Management System)の表や、スプレッドシートのデータに対応するデータ構造です。
この記事では、DataFrameの作り方、特にDataFrameコンストラクタへのデータの与え方に焦点を当てて紹介したいと思います。

pandasやDataFrameについては素敵な記事がたくさん公開されていますし、公式ドキュメントも充実しています。そちらをご覧ください。

1. DataFrameの作り方

DataFrameにはいくつの作り方があります。
方法を大別すると、pandas.DataFrameコンストラクタにデータを渡す方法、pandasに定義されている関数にデータを渡す方法の2種類があります。
また、入力データ形式は、リストのリスト辞書のリストNumPy配列CSVSQLなど、いろいろなバリエーションがあります。

具体例や詳細な使い方については既に公開されている記事や公式ドキュメントを参考にしてください。

DataFrameを作る方法をいくつか並べるとこんな感じでしょうか。結構たくさんありますね。

上記に含まれないものとしては、AWS Data Wranglerのような、pandasの外側のライブラリでDataFrameを生成・処理できるものも存在します。

2. DataFrameコンストラクタにデータを渡す方法

pandasではDataFrameコンストラクタにデータを与えることで、DataFrameを作成することができます。
公式ドキュメントを見ると、

data: ndarray (structured or homogeneous), Iterable, dict, or DataFrame

とあります。
つまり、ndarray(NumPy配列)Iterabledict(辞書)DataFrameからDataFrameを作れるということです。

2.1. ndarrayとDataFrameの場合

ndarrayやDataFrameはそれ自身2次元のデータであるため、DataFrameが生成できるのは直感的です。
例は公式ドキュメントにも載っています。

NumPy配列からDataFrameを作る例を示します。

import pandas as pd
import numpy as np

ndarray = np.array([[1, 2], [3, 4]])
pd.DataFrame(ndarray, columns=["x", "y"])
x y
0 1 2
1 3 4

DataFrameからDataFrameを作る例を示します。

import pandas as pd

df = pd.DataFrame([[1, 2], [3, 4]], columns=["x", "y"])
pd.DataFrame(df)
x y
0 1 2
1 3 4

2.2. dictの場合

一方、dictについては説明や例にもあるように、列名をキーとし列に含まれる値をバリューとするdictを想定しています。
dictからDataFrameを作る例を示します。

import pandas as pd

d = {"x": [1, 3], "y": [2, 4]}
pd.DataFrame(d)
x y
0 1 2
1 3 4

2.3. Iterableの場合

では、Iterableではどんな値のIterableを想定しているのでしょうか。
ただの確認不足かもしれませんが、このあたりドキュメントにははっきり書いてありません。
いくつか動いてほしいものを試してみましょう。

2.3.1. リストのリストを使う方法

最初に思いつくのは、リストのリストやタプルのリストです。
2次元配列をそのまま表現するなら最もシンプルです。
実は、DataFrameからDataFrameを作るところですでに出現しています。

import pandas as pd

l = [[1, 2], [3, 4]]

pd.DataFrame(l, columns=["x", "y"])
x y
0 1 2
1 3 4

この方法では、列名は自分で与える必要があるのが他の方法との差です。
与え忘れると、適当な列名になります。

import pandas as pd

l = [[1, 2], [3, 4]]

pd.DataFrame(l)
0 1
0 1 2
1 3 4

2.3.2. 辞書のリストを使う方法

次に思いつくのは、辞書のリストです。

import pandas as pd

l = [{"x": 1, "y": 2}, {"x": 3, "y": 4}]

pd.DataFrame(l)
x y
0 1 2
1 3 4

列名を与えなくても、辞書のキー名を列名だと思ってくれます。

2.3.3. dataclassを使う方法

dataclassはPython3.7で導入された機能で、データを保持するのが主目的のクラスを宣言するときに便利な機能です。
辞書と比べると、型アノテーションが利用出来たり、コードの可読性が上がったり、値をイミュータブルにするなど制限を付けることができるといったメリットがあります。
自作クラスと比べると、いろいろなメソッドを自分で書かなくてよくなるメリットがあります。例えば、__repr__を実装しなくても、printした際の出力がきれいです。もちろん、__repr__の実装を変更することもできます。

前置きが長くなりましたが、dataclassのリストはどうでしょうか?やってみましょう。

import pandas as pd
from dataclasses import dataclass

@dataclass
class Point:
  x: int
  y: int

p1 = Point(1, 2)
p2 = Point(3, 4)
l = [p1, p2]
pd.DataFrame(l)
x y
0 1 2
1 3 4

列名を与えなくても、フィールド名を列名だと思ってくれます。

dataclassについては、既に素敵な解説記事がたくさんあります。

2.3.4. namedtupleを使う方法

dataclassで行けるなら、namedtupleも行けるのではないか、そう考えるのは自然なことでしょう。

import pandas as pd
from collections import namedtuple

Point = namedtuple("Point", ["x", "y"])

p1 = Point(1, 2)
p2 = Point(3, 4)
l = [p1, p2]
pd.DataFrame(l)
x y
0 1 2
1 3 4

dataclassを全く同じ方法でDataFrameを生成することができました。
列名もばっちりです。

namedtupleについては、既に素敵な解説記事がたくさんあります。

2.4. ちょっとだけ深堀り

dataclassやnamedtupleを使わずに、単にクラスを定義したらどうなるでしょうか。

2.4.1. 自作クラスを使う方法(単純な実装)

とりあえず、メソッドなどを定義せずシンプルなクラスを作ってみます。

import pandas as pd

class Point:
  def __init__(self, x, y):
    self.x = x
    self.y = y

p1 = Point(1, 2)
p2 = Point(3, 4)
l = [p1, p2]
pd.DataFrame(l)
0
0 <\_\_main\_\_.Point object at 0x7f8f80bf5240>
1 <\_\_main\_\_.Point object at 0x7f8f80bf5198>

メソッドを何も定義していないので当然といえば当然ですが、いまいちですね。

2.4.2. コードを見てみる

どうすれば自作クラスの場合に「いい感じ」のDataFrameを取得できるのか調べてみましょう。

コンストラクタ

まず、DataFrameコンストラクタのコードはframe.py#L573-L776です。
第一引数のdataがどんな型やクラスなのか判定して、処理を分岐しています。
DataFrameなのか辞書なのかNumPy配列なのかなどを判定した後に、frame.py#L682-L726でここまで判定したもの以外のIterableに対する処理が記述されています。

dataclassの場合

すこし横道にそれて、dataclassに対する処理は、frame.py#L686-L687です。
特別扱いされていますね。
上記の延長線上で、list(map(asdict, data))が呼ばれて、これ以降は辞書のリストと同じ扱いです。

namedtupleの場合

さらに横道にそれて、namedtupleに対する処理は、frame.py#L694で呼び出されているnested_data_to_arraysconstruction.py#L480-L481です。
列名さえ取れてしまえば、あとはタプルのリストと同じ扱いです。

classの場合

元の道に戻ります。
ただのクラスの場合、コードのどこを通るでしょうか。

frame.py#L682のis_list_like(data)は、クラスのリストでもTrueになるはずです。
frame.py#L683-L684は、リストでないものをリスト化しているだけなのでスルーします。
frame.py#L685は、空でないリストを渡しているのでTrueです。
frame.py#L686-L687は、データクラスではないのでifの中には入りません。

frame.py#L688tread_as_nestedは、4つないし5つの条件をチェックしています。
重要なのはis_list_like(data[0])です。
これは、クラスのリストの0番目(=結局クラス)がis_list_likeかをチェックしています。
is_list_likeの公式ドキュメントコードのコメントを見ると、is_list_likeは文字列以外のIterableであればTrueを返します。
つまり、値をそれ以上展開すべきかどうかを判定しているということですね。

今回はクラスの中身、Pointクラスのx座標とy座標を展開してほしいとすると、tread_as_nestedがTureとなりその配下のブロックを通るようになってほしいということになります。
よって、まずはIterableにしてみましょう。

2.4.3. 自作クラスを使う方法(リスト劣化版)

Iterableにするために、__iter__と__next__を実装します。

import pandas as pd

class Point():
  def __init__(self, x, y):
    self.x = x
    self.y = y
    self.__n = 0
    
  def __iter__(self):
    self.__n = 0
    return self

  def __next__(self):
    if self.__n == 0:
      self.__n += 1
      return self.x
    elif self.__n == 1:
      self.__n += 1
      return self.y
    else:
      self.__n = 0
      raise StopIteration
    

p1 = Point(1, 2)
p2 = Point(3, 4)
l = [p1, p2]
pd.DataFrame(l)
0 1
0 1 2
1 3 4

リストを使った場合と同じところまでは来ました。
というよりは、リストの劣化版が出来ただけですが…。

2.4.4. 自作クラスを使う方法(さらなる改良)

construction.py#L796-L805の中で列名を解決してくれそうでpandasの型ではないものは、abc.Mappingくらいでしょうか。
公式ドキュメントによるとabc.Mappingは辞書のようなオブジェクトのための抽象クラスで、実装するのはなかなか大変です。
興味のある方は実装してみていただければと思います。

ここまでの調査結果を見ても、dataclassとして宣言してから拡張していくのがおすすめです。

3. 関数にデータを渡す方法

pandas.DataFrameやpandasには、データを読み込むのに便利な関数が定義されています。
これらの関数は、第一引数はおおむねデータの所在であるファイルパスかファイルオブジェクトです。
ファイルパスにはURLも指定出来たり、ファイルオブジェクトにExcelFileオブジェクトを指定してファイルを開いたままシートごとに処理をしたりすることもできます。

# 名前 説明
1 pandas.DataFrame.from_dict 辞書からDataFrameを作る関数。辞書のバリューが行データなのか列データなのかを指定できるが、指定しない場合はDataFrameコンストラクタを呼び出す。 frame.py#L1510-L1593
2 pandas.DataFrame.from_records 行データからDataFrameを作る関数。from_dict以外のコンストラクタframe.py#L1943-L2124
3 pandas.read_pickle Python独自のシリアライズ型式であるpickle形式のファイルでDataFrameを保存する。
4 pandas.read_table tsvファイルからデータを取り込むときに使う。区切り文字は変えることができる。read_csvとおおむね一緒。
5 pandas.read_csv csvファイルからデータを取り込むときに使う。区切り文字は変えることができる。read_tsvとおおむね一緒。
6 pandas.read_fwf 固定長レコードのデータを取り込むときに使う。
7 pandas.read_clipboard クリップボードからcsvファイルを読み込むときに使う。read_csvファイルのオプションをキーワード引数で指定できる。
8 pandas.read_excel xls形式やxlsx形式のファイルからデータを取り込むときに使う。シートを指定したり取り込むに使うエンジンを指定することもできる。
9 pandas.read_json jsonファイルからデータを取り込むときに使う。orient引数で読み込み方を調整できる。
10 pandas.read_html html形式の表からデータを取り込むときに使う。html内のテーブルをすべて対象とするため戻り値はDataFrameのリストである。htmlをパースするエンジンを指定できる。
11 pandas.read_xml xml形式のファイルからデータを取り込むときに使う。XPathを使ったり、xmlをパースするエンジンを指定できる。
12 pandas.read_hdf Hierarchical Data Format形式のファイルからデータを取り込むときに使う。
13 pandas.read_feather Apache ArrowのFeather形式のファイルからデータを取り込むときに使う。Feather形式は列指向のデータ格納形式である。
14 pandas.read_parquet Apache Parquetのparquet形式のファイルからデータを取り込むときに使う。parquet形式をパースするエンジンを指定できる。parquet形式は列指向のデータ格納形式である。
15 pandas.read_orc ORC形式のファイルからデータを取り込むときに使う。ORC形式は列指向のデータ格納形式である。
16 pandas.read_sas XPORTやSAS7BDAT形式のファイルからデータを取り込むときに使う。
17 pandas.read_spss SPSSのファイルからデータを取り込むときに使う。
18 pandas.read_sql_table RDBMSからデータを取り込むときに使う。テーブル名とコネクションを指定する。コネクションにはコネクションオブジェクトかURLを指定できるが、SQLALchemyがサポートしているRDBMSでないと使えないので注意が必要。
19 pandas.read_sql_query RDBMSからデータを取り込むときに使う。SQLとコネクションを指定する。コネクションにはコネクションオブジェクトかURLを指定できる。SQLite3のコネクションしかサポートしていないとあるが、DBAPIv2に則っていれば動作はする。
20 pandas.read_sql RDBMSからデータを取り込むときに使う。read_sql_tableおよびread_sql_queryのラッパーである。テーブル名が与えられた場合はread_sql_table、SQLが与えられた場合はread_sql_queryを呼び出す。
21 pandas.read_gbq Google BigQueryからデータを取り込むときに使う。SQLの実行結果をDataFrameとして取り込むことができる。
22 pandas.read_stata Stata形式のファイルからデータを取り込むときに使う。

まとめ

冒頭の結論をご覧ください。

19
21
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
19
21

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?