Pandas: 完全なコピーなしで混合dtypeDataFrameを構築する方法はありません、提案されたソリューション

作成日 2015年01月09日  ·  58コメント  ·  ソース: pandas-dev/pandas

何時間も髪を引き裂いた後、すべてのデータをコピーせずに混合dtype DataFrameを作成することは不可能であるという結論に達しました。つまり、何をしても、混合dtypeDataFrameを作成する場合は、必然的にデータの一時バージョンを作成し(たとえば、np.emptyを使用)、さまざまなDataFrameのコンストラクターが常にこの一時バージョンのコピーを作成します。 この問題は、1年前にすでに発生しています: https

これは、他のプログラミング言語との相互運用性にとって特にひどいものです。 たとえばCの呼び出しからDataFrameにデータを入力する場合、これを行う最も簡単な方法は、PythonでDataFrameを作成し、基になるデータ(np.arrays)へのポインターを取得して、これらのnpを渡すことです。 .arraysに沿って、データを入力できるようにします。 この状況では、DataFrameがどのデータから始まるかは気にしないでください。目標は、メモリを割り当てるだけなので、コピー先がわかります。

これは、原則として(特定の状況や実装の詳細などに応じて)、実際に必要な2倍のメモリを使用しないことを保証するのが難しいことを意味するため、一般的にイライラします。

これには、定量的なpythonスタックにすでに基づいている非常に単純なソリューションがあります。numpyの空に類似したメソッドがあります。 これによりスペースが割り当てられますが、実際には何かを書き込んだりコピーしたりする時間を無駄にすることはありません。 すでに空が取られているので、メソッドfrom_emptyを呼び出すことを提案します。 インデックス(必須、最も一般的なユースケースはnp.arange(N)を渡すことです)、列(必須、通常は文字列のリスト)、タイプ(列の受け入れ可能なタイプのリスト、列と同じ長さ)を受け入れます。 タイプのリストには、すべてのnumpy数値タイプ(int、float)のサポート、およびDatetimeIndexやCategoricalなどの特別なPandas列のサポートが含まれている必要があります。

追加のボーナスとして、実装は完全に別個のメソッドであるため、既存のAPIにまったく干渉しません。

API Design Constructors Dtypes

最も参考になるコメント

この機能を要求するSOには多くのスレッドがあります。

これらの問題はすべて、BlockManagerが個別の列を単一のメモリチャンク(「ブロック」)に統合することに起因しているように思われます。
copy = Falseが指定されている場合、データをブロックに統合しないのが最も簡単な修正ではありません。

非統合のモンキーパッチが適用されたBlockManagerがあります。
https://stackoverflow.com/questions/45943160/can-memmap-pandas-series-what-about-a-dataframe
私はこの問題を回避するために使用しました。

全てのコメント58件

インデックスと列を使用して空のフレームを作成するだけです
次に、ndarraysを割り当てます-これらは、特定のdtypeのすべてを一度に割り当てることをコピーしません

必要に応じて、np.emptyを使用してこれらを作成できます

df = pd.DataFrame(index=range(2), columns=["dude", "wheres"])

df
Out[12]:
  dude wheres
0  NaN    NaN
1  NaN    NaN

x = np.empty(2, np.int32)

x
Out[14]: array([6, 0], dtype=int32)

df.dude = x

df
Out[16]:
   dude wheres
0     6    NaN
1     0    NaN

x[0] = 0

x
Out[18]: array([0, 0], dtype=int32)

df
Out[19]:
   dude wheres
0     6    NaN
1     0    NaN

それは私にコピーしているように見えます。 私が書いたコードがあなたの意図したものではない場合、または発生したコピーが私が排除しようとしていると思ったコピーではない場合を除きます。

dtypeを変更しました
だからそれはフロートで試してコピーしました

y = np.empty(2, np.float64)

df
Out[21]:
   dude wheres
0     6    NaN
1     0    NaN

df.wheres = y

y
Out[23]: array([  2.96439388e-323,   2.96439388e-323])

y[0] = 0

df
Out[25]:
   dude         wheres
0     6  2.964394e-323
1     0  2.964394e-323

df = pd.DataFrame(index=range(2), columns=["dude", "wheres"])

df.dtypes
Out[27]:
dude      object
wheres    object
dtype: object

dtypeはオブジェクトであるため、floatとintのどちらを使用するかに関係なく変更されます。

In [25]: arr = np.ones((2,3))

In [26]: df = DataFrame(arr,columns=['a','b','c'])

In [27]: arr[0,1] = 5

In [28]: df
Out[28]: 
   a  b  c
0  1  5  1
1  1  1  1

混合型でw / oaコピーを作成することはできますが、かなり注意が必要です。 問題は、一部のタイプがコピーを必要とすることです(たとえば、メモリ競合の問題を回避するためのオブジェクト)。 また、内部構造はさまざまなタイプを統合しているため、新しいタイプを追加するにはコピーが必要になります。 ほとんどの場合、コピーを回避することは非常に困難です。

必要なものを作成し、データへのポインタを取得してから上書きするだけです。 なぜそれが問題なのですか?

問題は、必要なものを作成するために、正しいdtypeのものをコピーする必要があることです。このデータは、使用するつもりはありません。 空のDataFrameを作成するという提案が重要なRAMを使用しないと仮定しても、これはコピーのコストを軽減しません。 1ギガバイトのDataFrameを作成して別の場所に配置する場合は、メモリ内に1ギガバイトのガベージをコピーするコストを支払う必要がありますが、これはまったく不要です。 これを問題視しませんか?

はい、内部構造がさまざまなタイプを統合していることを理解しています。 メモリ競合の問題が何を意味するのか正確にはわかりませんが、いずれにせよ、オブジェクトはここで実際に関心のあるものではありません。

実際、一般的にコピーを回避することは難しい問題ですが、私が最初から必要なすべての情報を提供しているので、私が提案した方法でコピーを回避することはかなり簡単です。 データから構築するのと同じですが、データからdtypesと行数を推測してデータをコピーする代わりに、dtypesと行数を直接指定し、コピーを除いた場合とまったく同じように他のすべてを実行する点が異なります。

サポートされているすべての列タイプに対して「空の」コンストラクターが必要です。 numpyの数値タイプの場合、これは明らかです。Categoricalの場合はゼロ以外の作業が必要であり、DatetimeIndexについては不明です。

コンストラクターにdictを渡し、copy = Falseが機能するはずです

したがって、これは機能します。 ただし、渡す配列が別個のdtypeであることを確認する必要があります。 そして、これに対して何かを行うと、基になるデータがコピーされる可能性があります。 だからYMMV。 もちろん、私がいる1/0の代わりにnp.empty渡すことができます。

In [75]: arr = np.ones((2,3))

In [76]: arr2 = np.zeros((2,2),dtype='int32')

In [77]: df = DataFrame(arr,columns=list('abc'))

In [78]: df2 = DataFrame(arr2,columns=list('de'))

In [79]: result = pd.concat([df,df2],axis=1,copy=False)

In [80]: arr2[0,1] = 20

In [81]: arr[0,1] = 10

In [82]: result
Out[82]: 
   a   b  c  d   e
0  1  10  1  0  20
1  1   1  1  0   0

In [83]: result._data
Out[83]: 
BlockManager
Items: Index([u'a', u'b', u'c', u'd', u'e'], dtype='object')
Axis 1: Int64Index([0, 1], dtype='int64')
FloatBlock: slice(0, 3, 1), 3 x 2, dtype: float64
IntBlock: slice(3, 5, 1), 2 x 2, dtype: int32

In [84]: result._data.blocks[0].values.base
Out[84]: 
array([[  1.,  10.,   1.],
       [  1.,   1.,   1.]])

In [85]: result._data.blocks[1].values.base
Out[85]: 
array([[ 0, 20],
       [ 0,  0]], dtype=int32)

_ reindexがキャストを強制するため、最初の試行が削除されました。これは奇妙な「機能」です。_

'method'を使用する必要があります。これにより、この試行の満足度が少し低下します。

arr = np.empty(1, dtype=[('x', np.float), ('y', np.int)])
df = pd.DataFrame.from_records(arr).reindex(np.arange(100))

パフォーマンスが本当に心配な場合は、概念的にはるかに単純なので、なぜnumpyをできるだけ多く使用しないのかわかりません。

jreback、あなたの解決策に感謝します。 これは、Categoricals(私を驚かせた)でも機能するようです。 問題が発生した場合はお知らせします。 意味がわかりません。これに対して何かを行うと、コピーされる可能性があります。 どういう意味ですか? COWセマンティクスがない限り、構築時に、深いコピーと浅いコピーに関して得られるものが表示されると思います。

私はまだfrom_emptyコンストラクターを実装する必要があると思います。この手法は機能しますが、多くのコードオーバーヘッドが必要になるため、それほど難しいことではないと思います。 原則として、これは単一の複合dtypeと行数を指定することで実行できます。

bashtage、これらのソリューションはまだDataFrame全体に書き込みます。 書き込みは一般に読み取りよりも遅いため、これはせいぜい問題のオーバーヘッドの半分未満しか節約できないことを意味します。

明らかに、私がnumpyを使用したことがない場合、パンダには私が愛する多くの素晴らしい機能があり、それらをあきらめたくないからです。 あなたは本当に質問しましたか、それとも私がこのパフォーマンスヒットを受けたくないのであれば私がnumpyを使うべきだとほのめかしましたか?

これをスクラッチしてください、ユーザーエラー、そして私の謝罪。 copy = Falseを指定したreindex_axisは完全に機能しました。

bashtage、これらのソリューションはまだDataFrame全体に書き込みます。 書き込みは一般に読み取りよりも遅いため、これはせいぜい問題のオーバーヘッドの半分未満しか節約できないことを意味します。

本当ですが、 reindex新しいmethodに必要なものはすべて、何も入力されないため、書き込みやコピーを行わなくても、任意の列タイプで型付き配列を割り当てることができます。

明らかに、私がnumpyを使用したことがない場合、パンダには私が愛する多くの素晴らしい機能があり、それらをあきらめたくないからです。 あなたは本当に質問しましたか、それとも私がこのパフォーマンスヒットを受けたくないのであれば私がnumpyを使うべきだとほのめかしましたか?

それは少し修辞的でした-numpyはメモリのブロブとしてのデータアクセスに近づくのをはるかに簡単にするので、パフォーマンスの観点からも深刻な提案ですが、これは非常に書き込もうとしている場合に重要です高性能コード。 コードの単純さがパフォーマンスよりも重要な場合は、いつでもnumpyからpandasに変換できます。

あなたの言っていることがわかります。 それでも、回避策ではなく、よりクリーンにインターフェイスの一部にする必要があると思いますが、回避策が進むにつれて、それは優れたものであり、実装が簡単です。

パンダは、その主な目的が1つであるとしても、パフォーマンスを強調しています。 明らかに、それはnumpyと比較してより高いレベルの機能を持っており、それらは有料である必要があります。 私たちが話していることは、それらのより高いレベルの機能とは何の関係もありません、そしてあなたがそれらを必要としない場所で大量のコピーにお金を払うべき理由はありません。 この説明とはまったく異なる、列やインデックスなどの設定コストについて誰かが悪臭を放っている場合は、あなたの提案が適切です。

Pythonでメモリを割り当てるコードと比較して、書き込みのコストを過大評価していると思います。コストのかかる部分はメモリの割り当てです。 オブジェクトの作成にも費用がかかります。

どちらも1GBのメモリを割り当て、1つは空でもう1つはゼロです。

%timeit np.empty(1, dtype=[('x', float), ('y', int), ('z', float)])
100000 loops, best of 3: 2.44 µs per loop

%timeit np.zeros(1, dtype=[('x', float), ('y', int), ('z', float)])
100000 loops, best of 3: 2.47 µs per loop

%timeit np.zeros(50000000, dtype=[('x', float), ('y', int), ('z', float)])
100000 loops, best of 3: 11.7 µs per loop

%timeit np.empty(50000000, dtype=[('x', float), ('y', int), ('z', float)])
100000 loops, best of 3: 11.4 µs per loop

150,000,000の値をゼロにするための3µs。

次に、これらを簡単なDataFrameと比較します。

%timeit pd.DataFrame([[0]])
1000 loops, best of 3: 426 µs per loop

些細なことで約200倍遅くなります。 ただし、アレイが大きい場合はさらに悪化します。

%timeit pd.DataFrame(np.empty((50000000, 3)),copy=False)
1 loops, best of 3: 275 ms per loop

今では275ミリ秒かかります-これは何もコピーしていないことに注意してください。 コストはインデックスの設定などにありますが、配列が自明ではないほど大きい場合は明らかに非常に遅くなります。

パンダの他のオーバーヘッドが非常に大きいため、malloc + filliingコンポーネントのコストがゼロに近いため、これは私には時期尚早の最適化のように感じます。

タイトなループで何かを割り当てたい場合、パフォーマンス上の理由から、それはnumpy配列でなければならないようです。

わかりました、これが@quicknir 。 2つの問題。

  • #4464-これは基本的にDataFrameコンストラクターで複合dtypeを許可し、次に向きを変えてfrom_records()を呼び出します。これは、渡された配列がrec /構造化配列である場合にも呼び出すことができます-this基本的にfrom_records rec /構造化配列処理パスにします
  • copy=キーワードを介してfrom_records渡します
  • from_recordsは、上記のconcatソルンを使用できます。これは、rec-arrayを分割して(シリーズとして)サニタイズしてから、(dtypeブロックに)元に戻すのではなく、この部分です。内部で行われます)。

これはやや自明ではありませんが、混合型ですでに作成されたndarray(空の可能性があります)を非常に簡単に渡すことができます。 これは(最初のパスの実装では)(int / float / string)のみを処理する可能性が高いことに注意してください。 datetime / timedeltaは特別なサニタイズが必要であり、このslighltyをより複雑にするためです。

したがって、 @ bashtageはパフォーマンスの観点から正しいです。 必要に応じてフレームを作成し、ndarrayを変更することは非常に理にかなっています(ただし、ブロックを取得してこれを行う必要があります。そうしないと、コピーが取得されます)。

私が上で意味したのはこれです。 Pandasは、類似したdtype(たとえば、int64、int32は異なります)を「ブロック」(フレーム内の2-d)にグループ化します。 これらは連続したメモリndarrayです(現在単一のdtypeに対してのみ機能する単純に渡されない限り、新しく割り当てられます)。 次に、setitem(たとえば、 df['new_columns'] = 5を実行し、すでにint64ブロックがある場合、この新しい列は最終的にそれに連結されます(その結果、そのdtypeに新しいメモリが割り当てられます)。 これに関するビューとして参照を使用していた場合、それは無効になります。 そのため、これはDataFrame内部でピアリングなしで採用できる戦略ではありません。

@bashtageええ、あなたが指摘したように、大きなコストはインデックスです。 RangeIndex (#939を参照)は、この問題を完全に解決します。 (実際にはほとんど側枝で行われ、少しほこりを払う必要があります)。

最適化されたRangeIndexても、NumPyアレイを構築するよりも、2桁遅くなります。これは、はるかに重い重量の性質とDataFrame追加機能を考えると、十分に公平です。

これは便利な機能と見なすことができ、パフォーマンスの問題とは見なされないと思います。混合型DataFrameまたはPanelように初期化すると便利な場合があります。

dtype=np.dtype([('GDP', np.float64), ('Population', np.int64)])
pd.Panel(items=['AU','AT'],
         major_axis=['1972','1973'],
         minor_axis=['GDP','Population'], 
         dtype=[np.float, np.int64])

これはAPI /利便性の問題にすぎません

パフォーマンスは実際には偶発的な問題であることに同意しました(ドライバーではありません)

@bashtage

%timeit pd.DataFrame(np.empty((100、1000000)))
100ループ、ベスト3:ループあたり15.6ミリ秒

%timeit pd.DataFrame(np.empty((100、1000000))、copy = True)
1ループ、ベスト3:ループあたり302ミリ秒

したがって、データフレームへのコピーには、データフレームの作成に関連する他のすべての作業よりも20倍の時間がかかるようです。つまり、コピー(および追加の割り当て)は95%の時間です。 あなたが行ったベンチマークは正しいものをベンチマークしていません。 コピー自体または割り当てに時間がかかるかどうかは重要ではありません。重要なのは、単一のdtype DataFrameの場合と同じように、複数のdtype DataFrameのコピーを回避できれば、時間を大幅に節約できるということです。

あなたの2桁の推論もだまされています。 実行される操作はこれだけではありません。ディスクの読み取りなど、時間がかかる他の操作もあります。 現在、DataFrameを作成するために必要な追加のコピーは、データをディスクからDataFrameに読み込むだけの単純なプログラムで約半分の時間を費やしています。 1/20の時間がかかった場合、ディスクの読み取りが支配的であり(本来あるべきことですが)、それ以上の改善はほとんど効果がありません。

ですから、もう一度強調したいのですが、これは実際のパフォーマンスの問題です。

jrebackは、連結戦略がCategoricalsで機能しないことを考えると、上記で提案した改善が機能するとは思わないでください。 より良い出発点は、インデックスの再作成だと思います。 現在の問題は、インデックスの再作成によって多くの余分な処理が行われることです。 ただし、原則として、行数がゼロのDataFrameには、不要な作業を行うことなく、正しい行数のDataFrameを作成するために必要なすべての情報が含まれています。 ところで、これはパンダがスキーマオブジェクトを必要としているように私を本当に感じさせます、しかしそれは別の日の議論です。

同意しないことに同意する必要があると思います。 IMO DataFrameは、基本的なnumpy配列とDataFrameの作成との大きな違いの順序で示されているように、数値エコシステムにおける極端なパフォーマンスオブジェクトではありません。

%timeit np.empty((1000000, 100))
1000 loops, best of 3: 1.61 ms per loop

%timeit pd.DataFrame(np.empty((1000000,100)))
100 loops, best of 3: 15.3 ms per loop

現在、DataFrameを作成するために必要な追加のコピーは、データをディスクからDataFrameに読み込むだけの単純なプログラムで約半分の時間を費やしています。 1/20の時間がかかった場合、ディスクの読み取りが支配的であり(本来あるべきことですが)、それ以上の改善はほとんど効果がありません。

これは、DataFrameのパフォーマンスを気にする理由ではないと思います。100%無料にすることができたとしても、プログラムの合計時間は50%しか減少しません。

パフォーマンスの問題として考えたいのか、利便性の問題として考えたいのかに関わらず、この問題を解決するためにここでPRを行う余地があることに同意します。 私のPOVからは、パフォーマンスを気にするときは常にnumpy配列を使用するので、後者と見なします。 Numpyは、いくつかのことに比較的効率的なブロックマネージャーを使用しないなど、他のことも行います(列を追加して配列を拡張するなど)。 しかし、他の観点からは悪いです。

2つのオプションがあります。 1つ目は、上記の例のような空のコンストラクターです。 これは何もコピーしませんが、パンダの他のものと一致するようにおそらくヌルフィルになります。 ヌルフィリングはかなり安価であり、IMOの問題の根本にはありません。

もうDataFrame.from_blocksは、事前に形成されたブロックを使用してブロックマネージャーに直接渡すメソッド

DataFrame.from_blocks([np.empty((100,2)), 
                       np.empty((100,3), dtype=np.float32), 
                       np.empty((100,1), dtype=np.int8)],
                     columns=['f8_0','f8_1','f4_0','f4_1','f4_2','i1_0'],
                     index=np.arange(100))

このタイプのメソッドは、ブロックが互換性のある形状を持ち、すべてのブロックが一意のタイプを持ち、インデックスと列の形状の通常のチェックを行うことを強制します。 このタイプのメソッドはデータに対して何も行わず、BlockMangerで使用します。

@quicknirあなたはかなり複雑なものを組み合わせようとしています。 カテゴリカルはnumpyには存在せず、パンダの構成要素のような複合dtypeです。 次に、個別に作成して割り当てる必要があります(これは実際には非常に安価です。これらは、他の特異なdtypeのようにブロックに結合されません)。

@bashtagesolnは妥当なようです。 これにより、いくつかの簡単なチェックが提供され、データを単純に渡すことができます(そして他の内部ルーチンによって呼び出されます)。 通常、ユーザーは内部担当者を気にする必要はありません。 あなたは本当にしたいので、あなたはこれを認識する必要があります。

とはいえ、なぜあなたが望むようにフレームを作成しないのかはまだわかりません。 次に、ブロックポインタを取得し、値を変更します。 同じメモリが必要であり、 @ bathtageが指摘しているように、基本的にnullフレーム(すべてのdtype、index、columnsが設定されている)を作成するのはかなり安価です。

空のコンストラクターの意味はわかりませんが、行と目的のスキーマを使用せずにデータフレームを構築し、reindexを呼び出す場合、これはcopy = Trueで作成するのと同じ時間です。

2番目の提案は合理的ですが、カテゴリカルの実行方法を理解できる場合に限ります。 その件に関して、私はコードを調べていましたが、Categoricalsは統合できないことに気づきました。 そのため、私は整数配列と2つのカテゴリ系列を作成し、次に3つのDataFrameを作成し、3つすべてを連結しました。 案の定、2つのDataFrameが同じdtypeを持っていても、コピーは実行されませんでした。 これをDatetimeIndexで機能させる方法を試してみます。

@jreback私はまだあなたが望むようにフレームを作成するというあなたの意味に従わない。

@quicknir実際にやろうとしていることのコード/擬似コードのサンプルを見せてみませんか。

def read_dataframe(filename, ....):
   f = my_library.open(filename)
   schema = f.schema()
   row_count = f.row_count()
   df = pd.DataFrame.from_empty(schema, row_count)
   dict_of_np_arrays = get_np_arrays_from_DataFrame(df)
   f.read(dict_of_np_arrays)
   return df

以前のコードは、最初にnumpy配列の辞書を作成し、次にすべてをコピーしていたため、そこからDataFrameを作成していました。 それに約半分の時間が費やされていました。 だから私はそれをこのスキームに変えようとしています。 内容を気にせずに上記のようにdfを構築するのは非常に費用がかかるということです。

np配列の@quicknirdictには、多くのコピーが必要です。

あなたは単にこれをするべきです:

# construct your biggest block type (e.g. say you have mostly floats)
df = DataFrame(np.empty((....)),index=....,columns=....)

# then add in other things you need (say strings)
df['foo'] = np.empty(.....)

# say ints
df['foo2'] = np.empty(...)

これをdtypeで行うと、安くなります

それから。

for dtype, block in df.as_blocks():
    # fill the values
    block.values[0,0] = 1

これらのブロック値はnumpy配列へのビューであるため

タイプの構成は一般的に事前にわかっていません。最も一般的なユースケースでは、floatとintが適切に混在しています。 30個のfloat列と10個のint列がある場合、これがどのように安くなるかについては従わないと思います。そうです、floatは非常に安くなります。 しかし、intを実行するとき、私が見逃していることを一度に実行する方法がない限り、intの列をもう1つ追加するたびに、intブロック全体が再割り当てされます。

あなたが以前に私に与えた解決策はほぼ機能しているので、DatetimeIndexでうまくいくようには見えません。

空のコンストラクターの意味はわかりませんが、行と目的のスキーマを使用せずにデータフレームを構築し、reindexを呼び出す場合、これはcopy = Trueで作成するのと同じ時間です。

空のコンストラクタは次のようになります

dtype=np.dtype([('a', np.float64), ('b', np.int64), ('c', np.float32)])
df = pd.DataFrame(columns='abc',index=np.arange(100),dtype=dtype)

これにより、と同じ出力が生成されます

dtype=np.dtype([('a', np.float64), ('b', np.int64), ('c', np.float32)])
arr = np.empty(100, dtype=dtype)
df = pd.DataFrame.from_records(arr, index=np.arange(100))

データをコピーしないだけです。

基本的に、コンストラクターは、次の呼び出しに対して混合dtypeを許可しますが、これは機能しますが、単一の基本dtypeのみです。

df = pd.DataFrame(columns=['a','b','c'],index=np.arange(100), dtype=np.float32)

他の唯一の_feature_は、intの欠損値がないため、それらをオブジェクトdtypeに変換するという副作用があるint配列をnullで埋めないようにすることです。

2番目の提案は合理的ですが、カテゴリカルの実行方法を理解できる場合に限ります。 その件に関して、私はコードを調べていましたが、Categoricalsは統合できないことに気づきました。 そのため、私は整数配列と2つのカテゴリ系列を作成し、次に3つのDataFrameを作成し、3つすべてを連結しました。 案の定、2つのDataFrameが同じdtypeを持っていても、コピーは実行されませんでした。 これをDatetimeIndexで機能させる方法を試してみます。

from_blockメソッドは、統合のルールを知っている必要があるため、複数のカテゴリを許可しますが、他の基本タイプの1つだけを許可します。

うん...これはそれほど難しいことではありません....内部を穏やかに紹介したい人を探しています..... hint.hint.hint .... :)

ハハ、私はいくつかの実装作業を喜んで行います、誤解しないでください。 今週末は内部を調べて、どのコンストラクターが実装しやすいかを理解しようと思います。 まず、別のスレッドで発生しているDatetimeIndexの問題に対処する必要があります。

@quicknirこれに対する解決策を見つけましたか?

混合dtypeデータフレームを安価に割り当てて(ただし、塗りつぶさずに)、cythonライブラリの列をコピーなしで入力できるようにする方法を探しています。

私が始めるのを手伝ってくれるコードを(半ば機能していても)共有してくれたら素晴らしいと思います。

以下は賢明なアプローチでしょうか? プロトタイプのデータフレームから作業して、ブロッキングロジックの再作成を回避しました。

カテゴリカル以外に特別な処理が必要なdtypeはどれですか?

もちろん、作成されたデータフレームを使用することは、それが満たされるまで安全ではありません...

import numpy as np
from pandas.core.index import _ensure_index
from pandas.core.internals import BlockManager
from pandas.core.generic import NDFrame
from pandas.core.frame import DataFrame
from pandas.core.common import CategoricalDtype
from pandas.core.categorical import Categorical
from pandas.core.index import Index

def allocate_like(df, size, keep_categories=False):
    # define axes (waiting for #939 (RangeIndex))
    axes = [df.columns.values.tolist(), Index(np.arange(size))]

    # allocate and create blocks
    blocks = []
    for block in df._data.blocks:
        # special treatment for non-ordinary block types
        if isinstance(block.dtype, CategoricalDtype):
            if keep_categories:
                categories = block.values.categories
            else:
                categories = Index([])
            values = Categorical(values=np.empty(shape=block.values.shape,
                                                 dtype=block.values.codes.dtype),
                                 categories=categories,
                                 fastpath=True)
        # ordinary block types
        else:
            new_shape = (block.values.shape[0], size)
            values = np.empty(shape=new_shape, dtype=block.dtype)

        new_block = block.make_block_same_class(values=values,
                                                placement=block.mgr_locs.as_array)
        blocks.append(new_block)

    # create block manager
    mgr = BlockManager(blocks, axes)

    # create dataframe
    return DataFrame(mgr)


# create a prototype dataframe
import pandas as pd
a = np.empty(0, dtype=('i4,i4,f4,f4,f4,a10'))
df = pd.DataFrame(a)
df['cat_col'] = pd.Series(list('abcabcdeff'), dtype="category")

# allocate an alike dataframe
df1 = allocate_like(df, size=10)

@ ARF1は最終目標が何であるか本当に
簡単な例を提供できますか

copy = Falseとさらに連結すると、通常、これを回避します

@jreback cythonライブラリを使用して、圧縮されたデータストアから列ごとに大量のデータを読み取りたい。パフォーマンス上の理由から、中間コピーせずにデータフレームに直接解凍したい。

このような場合、通常のnumpyソリューションから借用して、データフレームにメモリを事前に割り当て、これらの割り当てられたメモリ領域へのポインタをcythonライブラリに渡して、に対応する通常のcポインタ/ c配列を使用できるようにします。これらのメモリ領域は、中間のコピー手順(または中間のPythonオブジェクトの生成)なしでデータフレームを直接埋めます。 リリースされたgilと並行して複数のcythonスレッドでデータフレームを埋めるオプションは、非常に有益です。

(簡略化された)擬似コードでは、idomは次のようになります。

df = fn_to_allocate_memory()
colums = df.columns.values
column_indexes = []
for i in xrange(len(df._data.blocks)):
    column_indexes.extend(df._data.blocks[i].mgr_locs.as_array)
block_arrays = [df._data.blocks[i].values for i in len(df._data.blocks)]

some_cython_library.fill_dataframe_with_content(columns, column_indexes, block_arrays)

これはあなたにとって意味がありますか?

私が理解しているように、 concatcopy=Falseは、同一のdtypeを持つ列をブロックに結合しませんが、その後の操作によってこれがトリガーされます。その結果、コピーを回避しようとしています。 それとも、パンダの内部操作を誤解しましたか?

大きな(塗りつぶされていない)データフレーム(係数〜6.7)のインスタンス化についてはある程度の進歩がありましたが、まだ数の多い速度にはほど遠いです。 あと90分の1だけ...

In [157]: a = np.empty(int(1e6), dtype=('i4,i4,f4,f4,f4,a10'))

In [158]: df = pd.DataFrame(a)

In [162]: %timeit np.empty(int(1e6), dtype=('i8,i4,i4,f4,f4,f4,a10'))
1000 loops, best of 3: 247 µs per loop

In [163]: %timeit allocate_like(df, size=int(1e6))
10 loops, best of 3: 22.4 ms per loop

In [164]: %timeit pd.DataFrame(np.empty(int(1e6), dtype=('i4,i4,f4,f4,f4,a10')))

10 loops, best of 3: 150 ms per loop

もう1つの希望は、このアプローチにより、少量のデータが頻繁に読み取られるときに、同じ形状のDataFrameをより迅速に繰り返しインスタンス化できるようになることです。 これまでのところ、これは主な目的ではありませんでしたが、うっかりして、これでより良い進歩を遂げました。

In [157]: a = np.empty(int(1e6), dtype=('i4,i4,f4,f4,f4,a10'))

In [158]: df = pd.DataFrame(a)

In [159]: %timeit np.empty(0, dtype=('i8,i4,i4,f4,f4,f4,a10'))
10000 loops, best of 3: 79.9 µs per loop

In [160]: %timeit allocate_like(df, size=0)
1000 loops, best of 3: 379 µs per loop

In [161]: %timeit pd.DataFrame(np.empty(0, dtype=('i4,i4,f4,f4,f4,a10')))
1000 loops, best of 3: 983 µs per loop

編集

上記のタイミングは、リンゴとオレンジを比較するときに非常に悲観的な絵を描いています。numpy文字列列は固定長のネイティブ文字列として作成されますが、パンダの同等の列はpythonオブジェクト配列として作成されます。 同様に比較すると、インスタンス化時間の約92%を占めるインデックス生成を除いて、DataFrameのインスタンス化が非常に高速になります。

@ ARF1 numpyの速度が必要な場合は、numpyを使用してください。 あなたが実際に何をしているのか、Cythonで何をしているのかわかりません。 通常の解決策は、計算をチャンク化するか、単一のdtypeをcythonに渡すか、または単に大きなマシンを取得することです。

DataFrameは、データの記述と操作の方法について、非常に多くのことを行っています。 それはあなたが実際に彼らとやっていることではありません。

ほとんどすべてのパンダ操作がコピーされます。 (ほとんどのnumpy操作と同様に)、何を求めているのかわからない。

@jreback私は現在numpyを使用していますが、構造化配列でのみ(便利に)処理できる混合dtypeがあります。 ただし、構造化配列は本質的に行優先の順序であり、これが私の典型的な分析次元と衝突してパフォーマンスが低下します。 Pandasは、列が主要な順序であるため、自然な代替手段のように見えます。データを適切な速度でデータフレームに取り込むことができれば。

もちろん、別の方法は、異なるdtypeのnumpy配列の辞書を使用することですが、スライスなどが不可能になるため、分析が面倒になります。

通常の解決策は、計算をチャンク化し、単一のdtypeをcythonに渡すことです。

これが、私の例のblock_arrays変数で行っていることです。

または単に大きなマシンを入手してください。

100倍以上速くなることは、私にとって少し経済的な課題です。 ;-)

@ ARF1あなたは物事がどのように機能するかについて非常に奇妙なモデルを持っています。 通常、少数のデータフレームを作成してから、それらを処理します。 作成速度は、実際の計算や操作のごく一部です。

@jreback :これは奇妙なモデルではありません。 純粋なPythonの観点から物事を見ると、おそらくそれは奇妙なモデルです。 C ++コードを使用している場合、データをPythonオブジェクトに読み込む最も簡単な方法は、既存のPythonオブジェクトへのポインターを渡すことです。 パフォーマンスに敏感なコンテキストでこれを行う場合は、Pythonオブジェクトを作成するための安価で安定した(メモリの場所の意味で)方法が必要です。

パンダのボードでこの態度が一般的である理由は正直わかりません。 残念なことに、パンダはnumpyよりも高レベルの構成であると理解している限り、パンダの「上」で開発する方が簡単な場合があります。 表形式のデータをPythonに吐き出したいCコードがある場合、パンダのDataFrameは、これまでで最も望ましいタイプであるため、これは本当に重要なユースケースのように思われます。

私が書いていることを否定的に受け取らないでください。パンダのDataFrameがそれほど素晴らしいとは思わなかった場合は、numpyレコードなどを使用して、それで完了します。

@ ARF1 :結局のところ、理由は覚えていませんが、私ができた最善の方法は、Copy = Falseを指定してnumpy配列から数値型ごとにデータフレームを作成し、Copy = Falseを指定してpandas.concatを再度使用することでした。それらを連結します。 numpy配列から単一タイプのDataFrameを作成する場合は、numpy配列の方向に十分注意してください。 向きが間違っていると、各列に対応するnumpy配列が自明ではなくストライドされ、パンダはこれを嫌い、最初の機会にコピーを作成します。 カテゴリカルは統合されず、フレームの残りの部分のコピーをトリガーしないため、最後にカテゴリカルに取り組むことができます。

この操作を段階的に実行し、基になるデータへのポインターを(基になるnumpy配列のarray_interfaceを介して)継続的に取得し、それらが同じであることを確認して、コピーが実際に削除されていることを確認する単体テストを作成することをお勧めします。 コピー/インプレースパラメータを尊重する必要がないというのは、パンダによる非常に残念な決定です。 つまり、DataFrameコンストラクターにたとえばcopy = Falseを設定した場合でも、パンダは、DataFrameを構築するために必要であると判断した場合、コピーを実行します。 パンダが引数を尊重できないときにスローする代わりにこれを行うという事実は、コピーを排除するコードを確実に書くことを非常に疲れさせ、非常に系統だったものにする必要があります。 検証する単体テストを作成しないと、後で誤って何かを微調整してコピーを作成する可能性があり、それがサイレントに発生してパフォーマンスが低下します。

あなたがそう言うなら@quicknir 。 物事を最適化する前に、単にプロファイルを作成する必要があると思います。 私が前に言ったように、そしてprobは再びなります。 建設時間は何も支配するべきではありません。 もしそうなら、あなたは物事を保持するためにDataFrameを使用しているだけです、それでそもそもそれを使用することのポイントは何ですか? それが支配的でない場合、問題は何ですか?

@jreback私がまだプロファイリングしていないと仮定して、あなたはそれを書きます。 実際、私は持っています。 同じデータ形式から表形式のデータを逆シリアル化するC ++とPythonのコードがあります。 Pythonコードには少しオーバーヘッドがあると思っていましたが、ディスクの読み取り時間が支配的であるため、違いは小さいはずだと思いました。 そうではありませんでした。コピーを最小限に抑えるために細心の注意を払って手直しする前は、PythonバージョンはC ++コードの2倍以下の時間がかかり、ほとんどすべてのオーバーヘッドはDataFrameの作成にありました。 つまり、気になるデータの読み取り、解凍、書き込みなど、内容をまったく気にしない非常に大きなサイズのDataFrameを作成するのにほぼ同じくらいの時間がかかりました。 これは非常にパフォーマンスが悪いです。

私が特定の操作を念頭に置いてこのコードのエンドユーザーであった場合、建設が支配的ではないことについてあなたが言っていることはおそらく有効でしょう。 実際には、私は開発者であり、このコードのエンドユーザーは他の人です。 彼らがDataFrameで何をするのか正確にはわかりません。DataFrameは、ディスク上のデータのメモリ内表現を取得する1つの方法です。 ディスク上のデータを使って非常に簡単なことをしたい場合でも、DataFrame形式を使用する必要があります。

明らかに、データを取得するためのより多くの方法(numpyコンストラクトなど)をサポートできますが、これによりコードの分岐が大幅に増加し、開発者としての作業がはるかに困難になります。 DataFrameを非常に遅くする必要がある根本的な理由があれば、私は理解し、DataFrame、numpy、またはその両方をサポートするかどうかを決定します。 しかし、それがそれほど遅くなる必要がある本当の理由はありません。 各タプルに列名とタイプ、および行数が含まれるタプルの配列を受け取るDataFrame.emptyメソッドを作成できます。

これが、サポートユーザーとライブラリライターの違いです。 ライブラリを作成するよりも、独自のコードを作成する方が簡単です。 また、他のライブラリライターではなく、ユーザーのみをライブラリでサポートする方が簡単です。 この場合、DataFrameの空の割り当ては、パンダのぶら下がっている果物であり、私のような人々や@ ARF1の生活を楽にするだろうと

妥当なテスト済みの文書化されたソルンが必要な場合は、すべての耳を使用してください。 pandasにはかなりの数のユーザー/開発者がいます。 これが、DataFrameが非常に用途が広い理由であり、多くのエラーチェックと推論が必要な理由と同じです。 上記のように何ができるかをご覧ください。

私はこれを実装するために少し時間を割くことをいとわないが、パンダ開発者の何人かからの設計に関して合理的なコンセンサスがある場合に限る。 プルリクエストを送信して、人々が変更したいことがある場合、それはすばらしいことです。 あるいは、10時間かけて、何かをきれいに行う方法がないことに気付いた場合、それを行う唯一の方法は、人々が不快だと思うことを伴う可能性があることです。それもクールです。 しかし、私はX時間を費やすのはあまりクールではなく、これはそれほど有用ではないと言われ、実装は厄介で、実際にクリーンアップできるとは思わない、コードベースを複雑にするなどです。私はこの感情から離れています。私はこれまでOSSプロジェクトに大きな貢献をしたことがないので、それがどのように機能するのかわかりません。 ちょうど私の最初の投稿で、私はまさにこれを提案し始めました、そしてそれから率直に言って、それはパンダにとって一種の「範囲外」であるという印象を受けました。

必要に応じて、新しい問題を開き、できるだけ具体的なデザイン提案を作成します。フィードバック/暫定的な承認が得られたら、可能な場合はそれに取り組みます。

@quicknir重要なことは、テストスイート全体に合格する必要があるということです。これは非常に包括的です。

これはパンダの範囲外ではありませんが、APIはある程度ユーザーフレンドリーでなければなりません。

なぜあなたが気に入らなかったのかわかりません

concat(list_of_arrays,axis=1,copy=False)これはあなたが望むことを正確に行うと私は信じています(そうでない場合は、実際に何をしたいのか明確にしないでください)。

私は最終的に同様の手法を使用しましたが、それぞれが異なるタイプの単一のnumpy配列から作成されたDataFrameのリストを使用しました。

まず、このテクニックを実行したときに、まだいくつかのコピーに遭遇したと思います。 私が言ったように、パンダは常にcopy = Falseを尊重するとは限らないので、コードが実際にコピーされているかどうかを確認するのは非常に疲れます。 パンダ17の場合、開発者がcopy = Trueをデフォルトにして、コピーを削除できないときにcopy = Falseがスローされることを本当に望んでいます。 しかしとにかく。

次に、別の問題は、後で列を並べ替える必要があることでした。 これは驚くほど厄介でした。コピーを作成せずにこれを行うことができる唯一の方法は、元々、列名を目的の最終順序で並べられた整数にすることでした。 次に、インデックスの並べ替えを行いました。 次に、列名を変更しました。

第三に、タイムスタンプタイプ(numpy datetime64)ではコピーが避けられないことがわかりました。

このコードは少し前に書いたので、頭の中で新鮮ではありません。 間違えた可能性もありますが、かなり丁寧に調べてみたところ、当時思いついた結果でした。

上記のコードは、numpy配列でも機能しません。 TypeError:非NDFrameオブジェクトを連結できません。 最初にそれらをDataFramesにする必要があります。

あなたがここで、または上記で与えた解決策が好きではないというわけではありません。 私はまだうまくいく簡単なものを見たことがありません。

@quicknir上記の私の例はうまく

pd.concat([np.zeros((2,2))]、axis = 1、copy = False)

私はパンダ0.15.2を使用しているので、おそらくこれは0.16で機能し始めましたか?

plsはpd.concatのdoc-stringを読み取ります。 DataFrameを渡す必要があります

ところでcopy=Trueがデフォルトです

そうです、それが私が書いたものです。 上で書いたコードスニペットには、list_of_dataframesではなくlist_of_arraysが含まれていました。 とにかく、私たちはお互いを理解していると思います。 私は最終的にpd.concatメソッドを使用しましたが、それは非常に簡単ではありません。人々をつまずかせるにはたくさんの落とし穴があります。

1)DataFrameのリストを作成する必要があります。 各DataFrameには、1つの異なるdtypeが必要です。 したがって、開始する前に、すべての異なるdtypeを収集する必要があります。

2)各DataFrameは、目的のdtype、同じ数の行、目的の列数、およびorder = 'F'フラグの単一のnumpy配列から作成する必要があります。 order = 'C'(デフォルト)の場合、パンダはコピーを作成することがよくありますが、そうでない場合はコピーを作成します。

3)無視1)カテゴリカルの場合、それらはブロックに統合されないため、後で追加できます。

4)すべての個別のDataFrameを作成する場合、列には、必要な順序を表す整数を使用して名前を付ける必要があります。そうしないと、コピーをトリガーせずに列の順序を変更できない場合があります。

5)DataFrameのリストを作成したら、concatを使用します。 copy = Falseは、コピーを省略できない場合はスローせず、静かにコピーするため、何も混乱していないことを入念に確認する必要があります。

6)必要な順序になるように列インデックスを並べ替えてから、列の名前を変更します。

私はこの手順を厳密に適用しました。 これはワンライナーではなく、間違いを犯す場所がたくさんあり、タイムスタンプではまだ機能しなかったと確信しています。また、インターフェイスだけを使用しないことで回避できる不要なオーバーヘッドがたくさんあります。 必要に応じて、パブリックAPIのみを使用して、この関数がどのように見えるかをドラフトで作成できます。いくつかのテストと組み合わせて、実際にコピーが削除されているかどうか、およびどのdtypeを使用しているかを確認できます。

また、copy = Falseは、たとえばDataFrameコンストラクターのデフォルトです。 私の主なポイントは、引数を尊重できない関数は、「合理的なことをする」のではなく、スローする必要があるということです。 つまり、copy = Falseを受け入れることができない場合は、例外をスローして、コピーの省略を実行できるように他の入力を変更するか、コピーをTrueに変更する必要があることをユーザーに知らせる必要があります。 copy = Falseの場合、コピーがサイレントに発生することはありません。これは驚くべきことであり、パフォーマンスを重視するユーザーがバグを見つけるのに役立ちません。

ここには必要のない多くのステップがあります
plsは私が上でしたように実際の例を示しています

numpyビューは、非常に単純な再形成操作(場合によっては)によってコピーを返すことができ、他の操作では返すことができないことを理解しています

コピーにはソフトな保証がありますが、これを保証するための多くの内省がなければ通常は不可能であり、定義上、単純で強力なパフォーマンスの高いコードの目的を無効にします。

DataFrame構造でのcopy=Falseの動作は、numpyのnp.array関数と一致しています(たとえば、配列のリストを指定すると、最終的なデータは常にコピーを作成します)。

これはパンダの残念な機能のギャップのようです。 私見ですが、パンダの内部(ブロックを統合する)の現在のモデルでは、満足のいく解決策はありません。 残念ながら、これは実際にはパンダのオプションではありません。パンダは、多数の列を持つDataFrameを作成する人々のために機能する必要があるためです。

必要なのは、各列を1Dnumpy配列として個別に格納する整頓されたデータを操作するために特別に設計された代替のDataFrame実装です。 これは、列をN次元配列にすることができることを除いて、実際にはX線のデータモデルにいくらか似ています。

スペースを割り当てるだけの一般的な高性能パンダデータフレームコンストラクターは、サポートする必要のあるさまざまな列タイプの範囲を考えると、重要であると思います。

そうは言っても、パンダのデータフレームを高性能データコンテナとして使用して、必要な列タイプに限定された割り当て専用のデータフレームコンストラクタを実装したいライブラリライターにとっては、かなり簡単に思えます。

次のコードセグメントは、インスピレーションとして役立ちます。 これにより、割り当てのみの、埋められていないデータフレームを、非常に高速な速度でインスタンス化できます。 コードにはPR#9977が必要であることに注意してください。

import numpy as np
from pandas.core.index import _ensure_index
from pandas.core.internals import BlockManager
from pandas.core.generic import NDFrame
from pandas.core.frame import DataFrame
from pandas.core.common import CategoricalDtype
from pandas.core.categorical import Categorical
from pandas.core.index import RangeIndex

def allocate_like(df, size, keep_categories=False):
    # define axes (uses PR #9977)
    axes = [df.columns.values.tolist(), RangeIndex(size)]

    # allocate and create blocks
    blocks = []
    for block in df._data.blocks:
        # special treatment for non-ordinary block types
        if isinstance(block.dtype, CategoricalDtype):
            if keep_categories:
                categories = block.values.categories
            else:
                categories = Index([])
            values = Categorical(values=np.empty(shape=block.values.shape,
                                                 dtype=block.values.codes.dtype),
                                 categories=categories,
                                 fastpath=True)
        # ordinary block types
        else:
            new_shape = (block.values.shape[0], size)
            values = np.empty(shape=new_shape, dtype=block.dtype)

        new_block = block.make_block_same_class(values=values,
                                                placement=block.mgr_locs.as_array)
        blocks.append(new_block)

    # create block manager
    mgr = BlockManager(blocks, axes)

    # create dataframe
    return DataFrame(mgr)

コンストラクターallocate_like()は、パフォーマンスの低下cf numpyは、大きな配列ではx2.3(通常はx333)、サイズがゼロの配列ではx3.3(通常はx8.9)です。

In [2]: import numpy as np

In [3]: import pandas as pd

In [4]: a = np.empty(int(1e6), dtype=('i4,i4,f4,f4,f4'))

# create template-dataframe
In [5]: df = pd.DataFrame(a)

# large dataframe timings
In [6]: %timeit np.empty(int(1e6), dtype=('i4,i4,f4,f4,f4'))
1000 loops, best of 3: 212 µs per loop

In [7]: %timeit allocate_like(df, size=int(1e6))
1000 loops, best of 3: 496 µs per loop

In [8]: %timeit pd.DataFrame(np.empty(int(1e6), dtype=('i4,i4,f4,f4,f4')))
10 loops, best of 3: 70.6 ms per loop

# zero-size dataframe timing
In [9]: %timeit np.empty(0, dtype=('i4,i4,f4,f4,f4'))
10000 loops, best of 3: 108 µs per loop

In [10]: %timeit allocate_like(df, size=0)
1000 loops, best of 3: 360 µs per loop

In [11]: %timeit pd.DataFrame(np.empty(0, dtype=('i4,i4,f4,f4,f4')))
1000 loops, best of 3: 959 µs per loop

申し訳ありませんが、しばらくの間これを見失いました。 @ ARF1 、上記のコードサンプルをありがとう

データなしでDataFrameのレイアウトに対応するクラスを作成すると、上記のようなコードがはるかに自然になり、おそらくパフォーマンスも向上すると本当に感じています。 このクラスは、たとえば行のインデックスの再作成を行うときにも再利用できます。

私が基本的に提案するのは、次のようなものです。DataFrameLayoutと呼ばれるクラスで、dtype、列名、および列の順序をラップします。 たとえば、dtypeから列番号(順序付け用)までのdictと、すべての名前を含む個別の配列を格納できます。 このレイアウトから、dictを単純でエレガントに繰り返すことで、ブロックマネージャーをすばやく作成できることがわかります。 このクラスは、空のコンストラクターのような場所で、またはインデックスの再作成操作で使用できます。

より複雑なデータには、このような抽象化が必要だと思います。 ある意味で、DataFrameは複合データ型であり、DataFrameLayoutは構成の正確な性質を指定します。

ちなみに、Categoricalsにも同様のことが必要だと思います。 つまり、カテゴリ、順序付けされているかどうか、バッキング配列タイプなどを格納するCategoricalType抽象化が必要です。つまり、実際のデータ以外のすべてです。 実際、DataFrameLayoutについて考えると、すべての列に完全に指定された型が必要であり、それが現在Categoricalsにとって問題であることがわかります。

人々はこれらの2つのクラスについてどう思いますか?

@quicknirすでにCategoricalDtypeクラスがあります-あなたが説明する完全なCategoricalType拡張できることに同意します。

DataFrameLayoutクラスについては完全にはわかりません。 基本的に、データフレームに代替のより単純なデータモデルを使用できると思います(RまたはJuliaでの方法に似ています)。 この種のことにある程度の関心があり、最終的には何らかの形で起こると思いますが、おそらくすぐには起こらないでしょう(そしておそらくパンダプロジェクトの一部としては決して起こらないでしょう)。

@quicknir yehDataFrameLayoutはここで車輪の再発明をしています。 すでにdtype仕様があります。

In [14]: tm.makeMixedDataFrame().to_records().dtype
Out[14]: dtype([('index', '<i8'), ('A', '<f8'), ('B', '<f8'), ('C', 'O'), ('D', '<M8[ns]')])

@jreback dtype仕様にはいくつかの大きな問題があるため、車輪の再発明ではありません。

1)私が見る限り、to_records()はDataFrame全体のディープコピーを実行します。 DataFrameの仕様(これからはこの用語を使用します)を取得するのは、安価で簡単なはずです。

2)to_recordsの出力はnumpyタイプです。 これが意味することの1つは、Categoricalsを適切にサポートするためにこれをどのように拡張できるかわからないということです。

3)仕様を内部的に保存するこの方法は、データがDataFrame内に(つまり、dtypeのようなブロックに)保存される方法と簡単に互換性がありません。 このような仕様からブロックを作成するには、dtypeから列番号までのdictを使用して、私が提案したような方法で仕様を保存することで回避できる多くの追加作業が必要になります。 2000列のDataFrameがある場合、これはコストがかかります。

つまり、レコード表現のdtypeは、適切な仕様がないための回避策です。 それはいくつかの重要な機能を欠いており、パフォーマンスの面ではるかに劣っています。

この機能を要求するSOには多くのスレッドがあります。

これらの問題はすべて、BlockManagerが個別の列を単一のメモリチャンク(「ブロック」)に統合することに起因しているように思われます。
copy = Falseが指定されている場合、データをブロックに統合しないのが最も簡単な修正ではありません。

非統合のモンキーパッチが適用されたBlockManagerがあります。
https://stackoverflow.com/questions/45943160/can-memmap-pandas-series-what-about-a-dataframe
私はこの問題を回避するために使用しました。

このページは役に立ちましたか?
0 / 5 - 0 評価