note.nkmk.me

pandas.DataFrame, SeriesとNumPy配列ndarrayを相互に変換

Date: 2018-03-11 / Modified: 2019-08-28 / tags: Python, pandas, NumPy

pandas.DataFrame, pandas.SeriesとNumPy配列numpy.ndarrayは相互に変換できる。

  • DataFrame, Seriesvalues属性でndarrayを取得
  • NumPy配列ndarrayからDataFrame, Seriesを生成
  • メモリの共有(ビューとコピー)の注意
  • pandas0.24.0以降: to_numpy()

それぞれについてサンプルコードとともに説明する。

なお、pandas.DataFrame, pandas.Seriesにはas_matrix()というnumpy.ndarrayを返すメソッドもあるが、バージョン0.23.0からdeprecated(非推奨)になっている。

スポンサーリンク

pandas.DataFrame, Seriesのvalues属性でNumPy配列ndarrayを取得

pandas.DataFrame, pandas.Seriesいずれもvaliues属性でNumPy配列numpy.ndarryを取得できる。なお、pandas0.24.0以降は最後に紹介するto_numpy()メソッドの使用が推奨されている。

pandas.DataFrameの場合。

import numpy as np
import pandas as pd

df = pd.DataFrame(data=[[1, 2, 3], [4, 5, 6]], columns=['a', 'b', 'c'])
print(df)
#    a  b  c
# 0  1  2  3
# 1  4  5  6

a_df = df.values
print(a_df)
# [[1 2 3]
#  [4 5 6]]

print(type(a_df))
# <class 'numpy.ndarray'>

print(a_df.dtype)
# int64

pandas.Seriesの場合。

s = df['a']
print(s)
# 0    1
# 1    4
# Name: a, dtype: int64

a_s = s.values
print(a_s)
# [1 4]

print(type(a_s))
# <class 'numpy.ndarray'>

print(a_s.dtype)
# int64

取得できるndarrayのデータ型dtypeは元のpandas.DataFrame, pandas.Seriesdtypeと同じ。

df_f = pd.DataFrame([[0.1, 0.2, 0.3], [0.4, 0.5, 0.6]])
print(df_f)
#      0    1    2
# 0  0.1  0.2  0.3
# 1  0.4  0.5  0.6

a_df_f = df_f.values
print(a_df_f)
# [[0.1 0.2 0.3]
#  [0.4 0.5 0.6]]

print(type(a_df_f))
# <class 'numpy.ndarray'>

print(a_df_f.dtype)
# float64

数値と文字列が混在するpandas.DataFrameobject型のndarrayとなる。

df_multi = pd.read_csv('data/src/sample_pandas_normal.csv')
print(df_multi)
#       name  age state  point
# 0    Alice   24    NY     64
# 1      Bob   42    CA     92
# 2  Charlie   18    CA     70
# 3     Dave   68    TX     70
# 4    Ellen   24    CA     88
# 5    Frank   30    NY     57

a_df_multi = df_multi.values
print(a_df_multi)
# [['Alice' 24 'NY' 64]
#  ['Bob' 42 'CA' 92]
#  ['Charlie' 18 'CA' 70]
#  ['Dave' 68 'TX' 70]
#  ['Ellen' 24 'CA' 88]
#  ['Frank' 30 'NY' 57]]

print(type(a_df_multi))
# <class 'numpy.ndarray'>

print(a_df_multi.dtype)
# object

ndarrayobject型についての詳細は以下の記事を参照。

DataFrameの特定の列だけndarrayに変換したい場合は、列を抽出したDataFramevaluesを取得すればよい。列の抽出については以下の記事を参照。

数値の列だけ選択するとndarrayの型もobjectではなくその型になる。

a_df_int = df_multi[['age', 'point']].values
print(a_df_int)
# [[24 64]
#  [42 92]
#  [18 70]
#  [68 70]
#  [24 88]
#  [30 57]]

print(type(a_df_int))
# <class 'numpy.ndarray'>

print(a_df_int.dtype)
# int64

場合によっては.Tで転置したほうが使いやすいかもしれない。

print(a_df_int.T)
# [[24 42 18 68 24 30]
#  [64 92 70 70 88 57]]

条件を指定して特定の型の列だけ抽出することもできる。例えばint64型の列のみ抽出したい場合は以下のように書ける。

a_df_int = df_multi.select_dtypes(include=int).values
print(a_df_int)
# [[24 64]
#  [42 92]
#  [18 70]
#  [68 70]
#  [24 88]
#  [30 57]]

print(type(a_df_int))
# <class 'numpy.ndarray'>

print(a_df_int.dtype)
# int64

select_dtypes()を使ったデータ型dtypeによる列の抽出についての詳細は以下の記事を参照。

そのほか、条件を満たす行名・列名の行・列のみを抽出することもできる。以下の記事を参照。

元のpandas.DataFrameと変換したnumpy.ndarrayでメモリを共有している場合があり、いずれかの値を変更するともう一方の値も変更されることがある。後述。

NumPy配列ndarrayからpandas.DataFrame, Seriesを生成

NumPy配列numpy.ndarrypandas.DataFrameおよびpandas.Seriesのコンストラクタの第一引数dataに指定できる。後述のように、numpy.ndarryと生成したpandas.DataFrame, pandas.Seriesはメモリを共有する。

pandas.Seriesを生成

コンストラクタに他の引数を何も指定しないと、元のndarrayの型のSeriesとなる。

import numpy as np
import pandas as pd

a = np.arange(4)
print(a)
# [0 1 2 3]

s = pd.Series(a)
print(s)
# 0    0
# 1    1
# 2    2
# 3    3
# dtype: int64

インデックス名indexSeriesの名前name、データ型dtypeなどを引数で指定することもできる。

index = ['A', 'B', 'C', 'D']
name = 'sample'
s = pd.Series(data=a, index=index, name=name, dtype='float')
print(s)
# A    0.0
# B    1.0
# C    2.0
# D    3.0
# Name: sample, dtype: float64

pandasのdtypeについては以下の記事を参照。

多次元のndarraySeriesのコンストラクタのdataに指定するとエラーになる。

a = np.arange(12).reshape((4, 3))
print(a)
# [[ 0  1  2]
#  [ 3  4  5]
#  [ 6  7  8]
#  [ 9 10 11]]

# s = pd.Series(a)
# print(s)
# Exception: Data must be 1-dimensional

例えば二次元のndarrayの行や列を選択してpandas.Seriesに変換することは可能。

s = pd.Series(a[2])
print(s)
# 0    6
# 1    7
# 2    8
# dtype: int64

s = pd.Series(a.T[2])
print(s)
# 0     2
# 1     5
# 2     8
# 3    11
# dtype: int64

pandas.DataFrameを生成

Seriesと同じく、コンストラクタに他の引数を何も指定しないと、元のndarrayの型のDataFrameとなる。

a = np.arange(12).reshape((4, 3))
print(a)
# [[ 0  1  2]
#  [ 3  4  5]
#  [ 6  7  8]
#  [ 9 10 11]]

df = pd.DataFrame(a)
print(df)
#    0   1   2
# 0  0   1   2
# 1  3   4   5
# 2  6   7   8
# 3  9  10  11

行名indexや列の名columns、データ型dtypeなどを引数で指定することもできる。

index = ['A', 'B', 'C', 'D']
columns = ['a', 'b', 'c']
df = pd.DataFrame(data=a, index=index, columns=columns, dtype='float')
print(df)
#      a     b     c
# A  0.0   1.0   2.0
# B  3.0   4.0   5.0
# C  6.0   7.0   8.0
# D  9.0  10.0  11.0

メモリの共有(ビューとコピー)の注意

values属性やコンストラクタで相互に変換したpandas.DataFramepandas.Seriesnumpy.ndarrayは互いにメモリを共有している場合がある。メモリが共有されている(片方がもう一方のビューである)と、一方の値を変更するともう一方の値も変更される。それぞれを別々に処理・利用したい場合は要注意。

以下のサンプルコードとその結果はpandas0.25.1でのもの。バージョンが違うと挙動が異なる場合がある。

values属性

まずは以下のような場合。

df = pd.DataFrame(data=[[1, 2, 3], [4, 5, 6]], columns=['a', 'b', 'c'])
print(df)
#    a  b  c
# 0  1  2  3
# 1  4  5  6

a_values = df.values
print(a_values)
# [[1 2 3]
#  [4 5 6]]

valuesはビューを返し、一方の値を変更するともう一方の値も変更される。np.shares_memory()で判定できる。

print(np.shares_memory(a_values, df))
# True

a_values[0, 0] = 100
print(a_values)
# [[100   2   3]
#  [  4   5   6]]

print(df)
#      a  b  c
# 0  100  2  3
# 1    4  5  6

常にビューを返すわけではなく、例えば各列のデータ型dtaypeが異なる場合はコピー。

df_if = pd.DataFrame(data=[[1, 0.1], [2, 0.2]], columns=['int', 'float'])
print(df_if)
#    int  float
# 0    1    0.1
# 1    2    0.2

print(df_if.dtypes)
# int        int64
# float    float64
# dtype: object

a_values_if = df_if.values
print(a_values_if)
# [[1.  0.1]
#  [2.  0.2]]

print(np.shares_memory(a_values_if, df_if))
# False

a_values_if[0, 0] = 100
print(a_values_if)
# [[100.    0.1]
#  [  2.    0.2]]

print(df_if)
#    int  float
# 0    1    0.1
# 1    2    0.2

そのほか、行や列などの範囲を指定してvaluesを取得する場合は指定方法によってビューかコピーかが異なったりする。

print(df[['a', 'c']].values)
# [[100   3]
#  [  4   6]]

print(np.shares_memory(df[['a', 'c']].values, df))
# False

print(df.iloc[:, ::2].values)
# [[100   3]
#  [  4   6]]

print(np.shares_memory(df.iloc[:, ::2].values, df))
# True

ビューだと問題がある場合はvsluescopy()で明示的にコピーを生成すればよい。

a_values_copy = df.values.copy()
print(a_values_copy)
# [[100   2   3]
#  [  4   5   6]]

print(np.shares_memory(a_values_copy, df))
# False

a_values_copy[0, 0] = 10
print(a_values_copy)
# [[10  2  3]
#  [ 4  5  6]]

print(df)
#      a  b  c
# 0  100  2  3
# 1    4  5  6

pandas.DataFrame, pandas.Seriesのコンストラクタ

コンストラクタpandas.DataFrame(), pandas.Series()numpy.ndarrayを渡すとメモリが共有される。一方の値を変更するともう一方の値も変更される。

a = np.array([[1, 2, 3], [4, 5, 6]])
print(a)
# [[1 2 3]
#  [4 5 6]]

df_a = pd.DataFrame(a, columns=['a', 'b', 'c'])
print(df_a)
#    a  b  c
# 0  1  2  3
# 1  4  5  6

print(np.shares_memory(a, df_a))
# True

a[0, 0] = 100
print(a)
# [[100   2   3]
#  [  4   5   6]]

print(df_a)
#      a  b  c
# 0  100  2  3
# 1    4  5  6

df_a.iat[1, 0] = 10
print(df_a)
#      a  b  c
# 0  100  2  3
# 1   10  5  6

print(a)
# [[100   2   3]
#  [ 10   5   6]]

メモリを共有させたくない場合はnumpy.ndarraycopy()でコピーをコンストラクタの引数に指定する。

df_a_copy = pd.DataFrame(a.copy(), columns=['a', 'b', 'c'])
print(df_a_copy)
#      a  b  c
# 0  100  2  3
# 1   10  5  6

a[0, 0] = 1
print(a)
# [[ 1  2  3]
#  [10  5  6]]

print(df_a_copy)
#      a  b  c
# 0  100  2  3
# 1   10  5  6

pandas0.24.0以降: to_numpy()

pandas0.24.0pandas.DataFrame, pandas.Seriesto_numpy()メソッドが追加された。上述のvalues属性と同様、numpy.ndarrayを返す。

公式ドキュメントではvalues属性ではなくto_numpy()メソッドの使用が推奨されているが、バージョン0.25.1時点ではvalues属性を使っても警告などは出ない。

df = pd.DataFrame(data=[[1, 2, 3], [4, 5, 6]], columns=['a', 'b', 'c'])
print(df)
#    a  b  c
# 0  1  2  3
# 1  4  5  6

a = df.to_numpy()
print(a)
# [[1 2 3]
#  [4 5 6]]

print(type(a))
# <class 'numpy.ndarray'>

デフォルトではvalues属性と同様、ビュー(メモリを共有するnumpy.ndarray)を返す場合がある。

print(np.shares_memory(df, a))
# True

a[0, 0] = 100
print(a)
# [[100   2   3]
#  [  4   5   6]]

print(df)
#      a  b  c
# 0  100  2  3
# 1    4  5  6

引数copyTrueとするとコピーが返される。デフォルトはcopy=False

a_copy = df.to_numpy(copy=True)
print(a_copy)
# [[100   2   3]
#  [  4   5   6]]

print(np.shares_memory(df, a_copy))
# False

a_copy[0, 0] = 10
print(a_copy)
# [[10  2  3]
#  [ 4  5  6]]

print(df)
#      a  b  c
# 0  100  2  3
# 1    4  5  6

デフォルト(copy=False)でもビューではなくコピーが返される場合もある。copy=Trueは必ずコピーを返すが、copy=Falseは必ずビューを返すとは限らない。

上述のvalues属性がコピーを返すような場合はデフォルト(copy=False)でもコピーとなる。

a_cols = df[['a', 'c']].to_numpy()
print(a_cols)
# [[100   3]
#  [  4   6]]

print(np.shares_memory(df, a_cols))
# False

引数dtypeでデータ型を指定可能。データ型が変更された場合もコピーを返す。

a_f = df.to_numpy(dtype=float)
print(a_f)
# [[100.   2.   3.]
#  [  4.   5.   6.]]

print(np.shares_memory(df, a_f))
# False
スポンサーリンク
シェア
このエントリーをはてなブックマークに追加

関連カテゴリー

関連記事