Pandas: لا توجد طريقة لإنشاء إطار DataFrame من النوع المختلط بدون نسخة كاملة ، الحل المقترح

تم إنشاؤها على ٩ يناير ٢٠١٥  ·  58تعليقات  ·  مصدر: pandas-dev/pandas

بعد ساعات من تمزيق شعري ، توصلت إلى استنتاج مفاده أنه من المستحيل إنشاء dtype DataFrame من النوع المختلط دون نسخ جميع بياناته فيه. وهذا يعني ، بغض النظر عما تفعله ، إذا كنت تريد إنشاء dtype DataFrame مختلط ، ستقوم حتما بإنشاء نسخة مؤقتة من البيانات (على سبيل المثال باستخدام np. تم طرح هذه المشكلة بالفعل ، منذ عام: https://github.com/pydata/pandas/issues/5902.

هذا أمر مروع بشكل خاص للتشغيل البيني مع لغات البرمجة الأخرى. إذا كنت تخطط لملء البيانات في DataFrame من على سبيل المثال استدعاء لـ C ، فإن أسهل طريقة للقيام بذلك هي إنشاء DataFrame في python ، والحصول على مؤشرات إلى البيانات الأساسية ، وهي np.arrays ، وتمرير هذه np .arrays بحيث يمكن ملؤها بالسكان. في هذه الحالة ، لا تهتم بالبيانات التي يبدأ بها DataFrame ، والهدف هو تخصيص الذاكرة فقط حتى تعرف ما الذي تنسخ إليه.

هذا أيضًا محبط بشكل عام لأنه يشير إلى أنه من حيث المبدأ (اعتمادًا على الموقف المحدد وتفاصيل التنفيذ ، إلخ) من الصعب ضمان أنك لن ينتهي بك الأمر باستخدام ضعف الذاكرة التي يجب عليك فعلها.

يحتوي هذا على حل بسيط للغاية مثبت بالفعل في مكدس البايثون الكمي: لديك طريقة مماثلة لطريقة numpy's الفارغة. هذا يخصص المساحة ، لكنه لا يضيع وقتًا في كتابة أو نسخ أي شيء. نظرًا لأن فارغًا مأخوذ بالفعل ، أقترح استدعاء الطريقة from_empty. سيقبل فهرسًا (إلزاميًا ، تكون حالة الاستخدام الأكثر شيوعًا هي تمرير np.arange (N)) ، والأعمدة (إلزامية ، عادةً قائمة السلاسل) ، والأنواع (قائمة الأنواع المقبولة للأعمدة ، بنفس طول الأعمدة). يجب أن تتضمن قائمة الأنواع دعمًا لجميع الأنواع العددية الصغيرة (ints ، floats) ، بالإضافة إلى أعمدة Pandas الخاصة مثل DatetimeIndex و Categorical.

كمكافأة إضافية ، نظرًا لأن التنفيذ يتم بطريقة منفصلة تمامًا ، فلن يتداخل مع واجهة برمجة التطبيقات الحالية على الإطلاق.

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. فارغة إذا كنت ترغب في ذلك

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 هو كائن ، لذلك تم تغييره بغض النظر عما إذا كنت أستخدم عددًا عشريًا أو 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 على نوع مختلط ولكنه أمر صعب للغاية. المشكلة هي أن بعض الأنواع تتطلب نسخة (على سبيل المثال ، كائن لتجنب مشاكل التنازع على الذاكرة). ويجمع الهيكل الداخلي أنواعًا مختلفة ، لذا فإن إضافة نوع جديد يتطلب نسخة. يعد تجنب النسخ أمرًا صعبًا للغاية في معظم الحالات.

يجب عليك فقط إنشاء ما تحتاجه ، والحصول على مؤشرات للبيانات ثم الكتابة فوقها. لماذا هذا يعد مشكلة؟

تكمن المشكلة في أنه من أجل إنشاء ما أحتاجه ، يجب أن أقوم بنسخ أشياء من النوع الصحيح ، البيانات التي ليس لدي أي نية لاستخدامها. حتى إذا افترضنا أن اقتراحك بإنشاء إطار بيانات فارغ لا يستخدم ذاكرة وصول عشوائي كبيرة ، فإن هذا لا يخفف من تكلفة النسخ. إذا كنت أرغب في إنشاء إطار بيانات بسعة 1 غيغابايت وتعبئته في مكان آخر ، فسأدفع تكلفة نسخ غيغابايت من القمامة في الذاكرة ، وهو أمر لا داعي له تمامًا. ألا ترى في هذا على أنه مشكلة؟

نعم ، أفهم أن الهيكل الداخلي يجمع أنواعًا مختلفة. لست متأكدًا تمامًا مما تقصده بقضايا التنازع على الذاكرة ، ولكن على أي حال ، فإن الأشياء ليست حقًا ما يهم هنا.

في الواقع ، في حين أن تجنب النسخ بشكل عام يمثل مشكلة صعبة ، فإن تجنبها بالطريقة التي اقترحتها أمر سهل إلى حد ما لأنني أوفر جميع المعلومات الضرورية من البداية. إنه مطابق للبناء من البيانات ، باستثناء أنه بدلاً من استنتاج أنواع dtypes وعدد الصفوف من البيانات ونسخ البيانات ، فإنك تحدد أنواع dtypes وعدد الصفوف مباشرةً ، وتفعل كل شيء آخر تمامًا كما كنت ستفعل بدون النسخة.

أنت بحاجة إلى مُنشئ "فارغ" لكل نوع عمود مدعوم. بالنسبة لأنواع الأرقام العددية ، يكون هذا واضحًا ، فهو يحتاج إلى عمل غير صفري لـ Categorical ، غير متأكد من DatetimeIndex.

تمرير إملاء إلى المُنشئ ونسخ = يجب أن يعمل False

لذلك هذا سوف يعمل. لكن عليك أن تكون متأكدًا من أن المصفوفات التي تمر عليها هي أنواع مميزة. وبمجرد أن تفعل أي شيء لهذا ، يمكن نسخ البيانات الأساسية. لذلك YMMV. يمكنك بالطبع تمرير 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 يفرض الإرسال ، وهي "ميزة" غريبة ._

يجب عليك استخدام "طريقة" ، مما يجعل هذه المحاولة أقل إرضاءً:

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

إذا كنت قلقًا حقًا بشأن الأداء ، فأنا لست متأكدًا من السبب وراء عدم استخدام المرء لـ numpy قدر الإمكان لأنه أبسط بكثير من الناحية المفاهيمية.

جرباك ، شكرًا لك على الحل. يبدو أن هذا يعمل ، حتى بالنسبة لـ Categoricals (التي فاجأتني). إذا واجهت مشكلات ، فسأعلمك بذلك. لست متأكدًا مما تقصده: إذا فعلت أي شيء لهذا ، فيمكنه نسخه. ماذا تقصد بأي شيء؟ ما لم تكن هناك دلالات COW ، أعتقد أن ما تراه هو ما تحصل عليه فيما يتعلق بالنسخ العميقة مقابل الضحلة ، في وقت البناء.

ما زلت أعتقد أنه يجب تنفيذ مُنشئ from_empty ، ولا أعتقد أنه سيكون بهذه الصعوبة ، بينما تعمل هذه التقنية ، فإنها تنطوي على الكثير من عبء التعليمات البرمجية. من حيث المبدأ ، يمكن القيام بذلك عن طريق تحديد نوع مركب واحد وعدد من الصفوف.

bashtage ، لا تزال هذه الحلول تكتب في DataFrame بأكمله. نظرًا لأن الكتابة عمومًا أبطأ من القراءة ، فهذا يعني في أحسن الأحوال أنها توفر أقل من نصف النفقات العامة المعنية.

من الواضح أنني إذا لم أذهب وأستخدم numpy ، فذلك لأن الباندا لديها العديد من الميزات والقدرات الرائعة التي أحبها ، ولا أريد التخلي عنها. هل كنت حقًا تسأل ، أو تشير فقط إلى أنه يجب علي استخدام numpy إذا كنت لا أرغب في الحصول على هذا الأداء؟

خدش هذا ، من فضلك ، خطأ المستخدم ، واعتذاري. reindex_axis مع copy = False يعمل على أكمل وجه.

bashtage ، لا تزال هذه الحلول تكتب في DataFrame بأكمله. نظرًا لأن الكتابة عمومًا أبطأ من القراءة ، فهذا يعني في أحسن الأحوال أنها توفر أقل من نصف النفقات العامة المعنية.

صحيح ، لكن كل ما تحتاجه إلى method مقابل reindex لن يمتلئ بأي شيء ، وبعد ذلك يمكنك تخصيص مصفوفة مطبوعة بأنواع أعمدة عشوائية دون كتابة / نسخ.

من الواضح أنني إذا لم أذهب وأستخدم numpy ، فذلك لأن الباندا لديها العديد من الميزات والقدرات الرائعة التي أحبها ، ولا أريد التخلي عنها. هل كنت حقًا تسأل ، أو تشير فقط إلى أنه يجب علي استخدام numpy إذا كنت لا أرغب في الحصول على هذا الأداء؟

لقد كان بلاغيًا بعض الشيء - على الرغم من أنه أيضًا اقتراح جاد من وجهة نظر الأداء لأن numpy يجعل من الأسهل كثيرًا الاقتراب من الوصول إلى البيانات باعتباره نقطة من الذاكرة وهو أمر مهم إذا كنت تحاول الكتابة كثيرًا كود عالي الأداء. يمكنك دائمًا التحويل من numpy إلى pandas عندما تكون بساطة الكود أكثر أهمية من الأداء.

أرى ما تقوله. ما زلت أعتقد أنه يجب أن يكون جزءًا من الواجهة بشكل أكثر وضوحًا بدلاً من حل بديل ، ولكن مع استمرار الحلول ، فهو حل جيد وسهل التنفيذ.

لا تزال Pandas تؤكد على الأداء كأحد أهدافه الرئيسية. من الواضح أنه يحتوي على ميزات ذات مستوى أعلى مقارنةً بغيره ، ويجب دفع ثمنها. ما نتحدث عنه ليس له علاقة بتلك الميزات ذات المستوى الأعلى ، ولا يوجد سبب يدفع المرء مقابل الحصول على نسخ ضخمة في الأماكن التي لا تحتاج إليها. سيكون اقتراحك مناسبًا إذا كان هناك شخص ما ينتقد حول تكلفة إعداد الأعمدة ، والفهرس ، وما إلى ذلك ، والتي تختلف تمامًا عن هذه المناقشة.

أعتقد أنك تبالغ في تقدير تكلفة الكتابة مقابل كود تخصيص الذاكرة في بايثون - الجزء المكلف هو تخصيص الذاكرة. إنشاء الكائن مكلف أيضًا.

كلاهما يخصص ذاكرة 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

3µs لتصفير 150.000.000 قيمة.

قارن الآن هذه مع 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 يقترب من 0 التكلفة.

يبدو أنك إذا كنت ترغب في تخصيص أي شيء في حلقة ضيقة يجب أن يكون مصفوفة غير معقدة لأسباب تتعلق بالأداء.

حسنًا ، هذا ما أعتقد أنه يجب علينا فعله ، quicknir إذا كنت ترغب في إجراء بعض التحسينات. 2 قضايا.

  • # 4464 - يسمح هذا بشكل أساسي للنوع المركب في المُنشئ DataFrame ثم الالتفاف واستدعاء from_records() ، والذي يمكن أيضًا استدعاؤه إذا كان المصفوفة التي تم تمريرها عبارة عن مصفوفة مُنظمة / مُنظمة - هذا سيجعل أساسًا from_records مسار معالجة المصفوفة المُنظَّمة / rec /
  • تمرير من خلال الكلمة الرئيسية copy= إلى from_records
  • from_records ويمكن بعد ذلك استخدام concat soln أن تبين لي أعلاه، بدلا من تقسيم وتفصيل مصفوفة فوق، التعقيم لهم (كما سلسلة) ثم جمعهم معا مرة أخرى (في dtype كتل، وهذا جزء يتم داخليًا).

هذا ليس تافهًا إلى حد ما ولكنه سيسمح بعد ذلك للمرء بالمرور في ndarray تم إنشاؤه بالفعل (يمكن أن يكون فارغًا) بأنواع مختلطة بسهولة كبيرة. لاحظ أن هذا من المحتمل (في تنفيذ المرور الأول) معالجة فقط (int / float / string). لأن datetime / timedelta بحاجة إلى تعقيم خاص وسيجعل هذا slighlty أكثر تعقيدًا.

لذا فإن bashtage صحيح من منظور الأداء. من المنطقي جدًا إنشاء الإطار كما تريد ثم تعديل ndarrays (ولكن يجب عليك القيام بذلك عن طريق الاستيلاء على الكتل ، وإلا ستحصل على نسخ).

ما قصدته أعلاه هو هذا. تقوم Pandas بتجميع أي نوع dtype (على سبيل المثال int64 و int32 مختلفان) في "كتلة" (2-d في إطار). هذه ذاكرة متجاورة ndarray (التي تم تخصيصها حديثًا ، ما لم يتم تمريرها ببساطة والتي تعمل حاليًا فقط لنوع واحد). إذا قمت بعد ذلك بعمل عنصر معين ، على سبيل المثال df['new_columns'] = 5 وكان لديك بالفعل كتلة int64 ، فسيتم ربط هذا العمود الجديد به في النهاية (مما يؤدي إلى تخصيص ذاكرة جديد لهذا النوع dtype). إذا كنت تستخدم مرجعًا كطريقة عرض حول هذا فلن يكون صالحًا بعد الآن. هذا هو السبب في أن هذه ليست استراتيجية يمكنك استخدامها دون النظر إلى البيانات الداخلية DataFrame.

bashtage نعم التكلفة الكبيرة هي المؤشر كما RangeIndex (انظر # 939) أن يحل هذه المشكلة تمامًا. (يتم في الواقع تقريبًا في فرع جانبي ، فقط يحتاج إلى بعض إزالة الغبار).

حتى مع RangeIndex المحسّن ، سيظل الأمر أبطأ بمرتين من إنشاء مصفوفة NumPy ، وهو أمر عادل بما يكفي نظرًا لطبيعة الوزن الأثقل بكثير والقدرات الإضافية لـ DataFrame .

أعتقد أن هذا يمكن اعتباره فقط وظيفة ملائمة ، وليس مشكلة في الأداء. قد يكون من المفيد تهيئة نوع مختلط DataFrame أو Panel like.

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

النسبة المئوية للوقت PDD DataFrame (np.empty ((100، 1000000)))
100 حلقة ، أفضل من 3: 15.6 مللي ثانية لكل حلقة

النسبة المئوية للوقت pd.DataFrame (np.empty ((100، 1000000)) ، copy = True)
حلقات واحدة ، أفضل من 3: 302 مللي ثانية لكل حلقة

لذا يبدو أن النسخ في إطار بيانات يستغرق 20 مرة أكثر من كل الأعمال الأخرى التي ينطوي عليها إنشاء إطار البيانات ، أي أن النسخة (والتخصيص الإضافي) تستغرق 95٪ من الوقت. المعايير التي لم تقم بقياس الشيء الصحيح. سواء كانت النسخة نفسها أو التخصيص هو ما يستغرق وقتًا لا يهم حقًا ، فالنقطة هي أنه إذا كان بإمكاني تجنب النسخ الخاصة بإطار DataFrame متعدد الأنواع بالطريقة التي يمكنني بها لنوع واحد DataFrame يمكنني توفير قدر كبير من الوقت.

كما أن درجة استدلالك من حيث الحجم مخادعة. هذه ليست العملية الوحيدة التي يتم إجراؤها ، فهناك عمليات أخرى يتم إجراؤها وتستغرق وقتًا ، مثل قراءة القرص. في الوقت الحالي ، تستغرق النسخة الإضافية التي أحتاج إلى القيام بها لإنشاء DataFrame حوالي نصف الوقت في برنامجي البسيط الذي يقرأ فقط البيانات من القرص إلى DataFrame. إذا استغرق الأمر 1/20 من الوقت ، فستكون قراءة القرص هي السائدة (كما ينبغي) ولن يكون هناك أي تأثير تقريبًا للتحسينات الإضافية.

لذلك أود أن أؤكد مرة أخرى لكليكما: هذه مشكلة أداء حقيقية.

jreback ، نظرًا لأن استراتيجية التسلسل لا تعمل مع الفئات ، فلا تعتقد أن التحسينات التي اقترحتها أعلاه ستنجح. أعتقد أن نقطة البداية الأفضل ستكون إعادة الفهرسة. المشكلة الآن هي أن reindex يقوم بالكثير من الأشياء الإضافية. ولكن من حيث المبدأ ، فإن DataFrame الذي يحتوي على صفوف صفرية يحتوي على جميع المعلومات اللازمة للسماح بإنشاء DataFrame بالعدد الصحيح من الصفوف ، دون القيام بأي عمل غير ضروري. راجع للشغل ، هذا يجعلني أشعر حقًا أن الباندا تحتاج إلى كائن مخطط ، لكن هذه مناقشة ليوم آخر.

أعتقد أننا سوف نتفق على الاختلاف. لا تعد إطارات بيانات IMO كائنات أداء متطرف في النظام البيئي الرقمي ، كما هو موضح بترتيب الاختلاف الكبير بين مصفوفة أساسية صغيرة وإنشاء 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٪ فقط.

أوافق على أن هناك مجالًا لك للقيام بعلاقات عامة هنا لحل هذه المشكلة ، سواء كنت تريد اعتبارها مشكلة أداء أو مشكلة ملائمة. من وجهة نظري ، أرى أنها الأخيرة لأنني سأستخدم دائمًا مصفوفة معقدة عندما أهتم بالأداء. يقوم Numpy بأشياء أخرى مثل عدم استخدام مدير كتلة فعال نسبيًا لبعض الأشياء (مثل زيادة المصفوفة عن طريق إضافة أعمدة). لكنها سيئة من وجهات نظر أخرى.

يمكن أن يكون هناك خياران. الأول ، مُنشئ فارغ كما في المثال الذي ذكرته أعلاه. هذا لن ينسخ أي شيء ، ولكن من المحتمل أن يتم ملء Null-F ليكون متسقًا مع الأشياء الأخرى في الباندا. الملء الفارغ رخيص جدًا وليس أصل المشكلة 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 أنت تحاول الجمع بين أشياء معقدة جدًا. لا توجد Categorical في numpy ، بل هي نوع مركب مثل هذا هو بناء الباندا. يجب عليك إنشاء وتعيين ثم بشكل منفصل (وهو في الواقع رخيص جدًا - لا يتم دمجها في كتل مثل أنواع dtype الأخرى).

bashtage يبدو

بعد كل ما قيل ، ما زلت غير متأكد من سبب عدم إنشاء إطار كما تريد تمامًا. ثم احصل على مؤشرات الكتلة وقم بتغيير القيم. إنها تكلف نفس الذاكرة ، وكما يشير bashtage ، فإن هذا رخيص جدًا لإنشاء إطار فارغ (يحتوي على كل نوع dtype والفهرس والأعمدة) الذي تم تعيينه بالفعل.

لست متأكدًا مما تقصده بالمُنشئ الفارغ ، ولكن إذا كنت تقصد إنشاء إطار بيانات بدون صفوف والمخطط المطلوب واستدعاء reindex ، فهذا هو نفس مقدار الوقت الذي تستغرقه الإنشاء باستخدام نسخة = صحيح.

اقتراحك الثاني معقول ، ولكن فقط إذا كنت تستطيع معرفة كيفية عمل الفئات. حول هذا الموضوع ، كنت أتصفح الكود وأدركت أن الفئات غير قابلة للدمج. لذا على حدس ، قمت بإنشاء مصفوفة عدد صحيح وسلسلتين فئويتين ، ثم قمت بإنشاء ثلاثة إطارات بيانات ، وسلسلة كل ثلاثة. من المؤكد أنه لم يقم بإجراء نسخة على الرغم من أن اثنين من إطارات البيانات لهما نفس النوع. سأحاول معرفة كيفية جعل هذا يعمل لمؤشر التاريخ والوقت.

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

كان الكود السابق يقوم بإنشاء قاموس من المصفوفات غير الدقيقة أولاً ، ثم إنشاء DataFrame من ذلك لأنه كان ينسخ كل شيء. تم إنفاق حوالي نصف الوقت على ذلك. لذلك أحاول تغييره إلى هذا المخطط. الشيء هو أن إنشاء df على النحو الوارد أعلاه حتى عندما لا تهتم بالمحتويات يعد مكلفًا للغاية.

يتطلبquicknir ديكت لمصفوفات np الكثير من النسخ.

يجب عليك ببساطة القيام بذلك:

# 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

نظرًا لأن قيم الكتلة هذه هي عروض في مصفوفات غير مرتبة

لا يُعرف تكوين الأنواع مسبقًا بشكل عام ، وفي حالة الاستخدام الأكثر شيوعًا ، يوجد مزيج صحي من العوامات و ints. أعتقد أنني لا أتابع كيف سيكون هذا رخيصًا ، إذا كان لدي 30 عمودًا عائمًا و 10 أعمدة كثافة ، إذن نعم ، ستكون العوامات رخيصة جدًا. ولكن عندما تقوم بتنفيذ المهام ، ما لم تكن هناك طريقة ما للقيام بها جميعًا في وقت واحد ، فأنا في عداد المفقودين ، في كل مرة تضيف عمودًا آخر من ints ، سيؤدي ذلك إلى إعادة تخصيص كتلة int بالكامل.

الحل الذي قدمته لي سابقًا قريب من العمل ، لا يبدو أنني أجعله يعمل مع DatetimeIndex.

لست متأكدًا مما تقصده بالمُنشئ الفارغ ، ولكن إذا كنت تقصد إنشاء إطار بيانات بدون صفوف والمخطط المطلوب واستدعاء reindex ، فهذا هو نفس مقدار الوقت الذي تستغرقه الإنشاء باستخدام نسخة = صحيح.

سيبدو المُنشئ فارغًا

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))

فقط لن ينسخ البيانات.

في الأساس ، سيسمح المُنشئ بنوع مختلط للمكالمة التالية التي تعمل ولكن فقط نوع أساسي واحد.

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

الميزة الأخرى الوحيدة هي منعها من الملء الخالي لمصفوفات int التي لها تأثير جانبي لتحويلها إلى نوع كائن نظرًا لعدم وجود قيمة مفقودة لـ ints.

اقتراحك الثاني معقول ، ولكن فقط إذا كنت تستطيع معرفة كيفية عمل الفئات. حول هذا الموضوع ، كنت أتصفح الكود وأدركت أن الفئات غير قابلة للدمج. لذا على حدس ، قمت بإنشاء مصفوفة عدد صحيح وسلسلتين فئويتين ، ثم قمت بإنشاء ثلاثة إطارات بيانات ، وسلسلة كل ثلاثة. من المؤكد أنه لم يقم بإجراء نسخة على الرغم من أن اثنين من إطارات البيانات لهما نفس النوع. سأحاول معرفة كيفية جعل هذا يعمل لمؤشر التاريخ والوقت.

يجب أن تعرف طريقة from_block قواعد الدمج ، بحيث تسمح بفئات متعددة ، ولكن واحدة فقط من الأنواع الأساسية الأخرى.

نعم ... هذا ليس بالأمر الصعب .... أبحث عن شخص يريد أن يكون لديه مقدمة لطيفة للأجزاء الداخلية ..... تلميح. تلميح. تلميح .... :)

هاها ، أنا على استعداد للقيام ببعض أعمال التنفيذ ، لا تفهموني بشكل خاطئ. سأحاول إلقاء نظرة على العناصر الداخلية في نهاية هذا الأسبوع والتعرف على المُنشئ الذي يسهل تنفيذه. أولاً ، على الرغم من أنني بحاجة للتعامل مع بعض مشكلات DatetimeIndex التي أواجهها في سلسلة منفصلة.

quicknir هل وجدت حلاً لهذا؟

إنني أبحث عن طريقة لتخصيص (ولكن ليس ملء) إطار بيانات مختلط نوع dtyframe بثمن بخس للسماح بنسخ أقل من الأعمدة من مكتبة cython.

سيكون رائعًا إذا كنت على استعداد لمشاركة أي رمز لديك (حتى شبه عامل) لمساعدتي في البدء.

هل ما يلي هو نهج معقول؟ لقد اتخذت خطوات جانبية لإعادة إنشاء منطق الحظر من خلال العمل من نموذج أولي لإطار البيانات.

ما هي الأنواع التي تحتاج إلى معالجة خاصة بصرف النظر عن الفئات؟

بالطبع ، استخدام إطار البيانات الذي تم إنشاؤه ليس آمنًا حتى يتم ملؤه ...

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 لست متأكدا حقا ما هو الهدف النهائي
هل يمكنك تقديم مثال بسيط

مزيد من concat مع copy = False سوف يتقدم بشكل عام إلى جانب هذا

jreback أريد استخدام مكتبة cython لقراءة بيانات كبيرة الحجم عمودًا تلو الآخر من مخزن بيانات مضغوط أريد فك ضغطه مباشرةً في إطار بيانات بدون نسخ وسيط لأسباب تتعلق بالأداء.

بالاقتراض من الحل العادي المعتاد في مثل هذه الحالات ، أريد تخصيص الذاكرة مسبقًا لإطار البيانات حتى أتمكن من تمرير المؤشرات إلى مناطق الذاكرة المخصصة هذه إلى مكتبة cython الخاصة بي والتي يمكنها بعد ذلك استخدام مؤشرات c / c-arrays العادية المقابلة لـ مناطق الذاكرة هذه لملء إطار البيانات مباشرة دون خطوات نسخ وسيطة (أو توليد كائنات بيثون وسيطة). سيكون خيار ملء إطارات البيانات بخيوط سايثون متعددة بالتوازي مع جيل تم إصداره بمثابة فائدة هامشية.

في الشفرة الزائفة (المبسطة) ، سيكون المصطلح شيئًا مثل:

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)

هل هذا منطقي بالنسبة لك؟

كما أفهم ، فإن concat مع copy=False لن يدمج الأعمدة ذات الأنواع المتماثلة في كتل ولكن العمليات أسفل السطر ستؤدي إلى هذا - مما يؤدي إلى النسخ الذي أحاول تجنبه. أم أنني أسيء فهم العملية الداخلية للباندا؟

على الرغم من أنني أحرزت بعض التقدم في إنشاء مثيل لأطر البيانات الكبيرة (غير المملوءة) (العامل ~ 6.7) إلا أنني ما زلت بعيدًا عن السرعات الصغيرة. فقط عامل آخر ~ 90 للذهاب ...

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

كان الأمل الآخر هو أن هذا النهج قد يسمح أيضًا بتكرار إنشاء مثيل أسرع لأطر DataFrames متشابهة الشكل عند قراءة البيانات ذات الحجم الصغير بشكل متكرر. لم يكن هذا هو الهدف الرئيسي حتى الآن ، لكنني أحرزت تقدمًا أفضل عن غير قصد مع هذا: فقط عامل قدره 4.8 ~ للذهاب إلى سرعة غير متوقعة.

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

تعديل

التوقيتات المذكورة أعلاه ترسم صورة متشائمة للغاية لأنها تقارن التفاح بالبرتقال: بينما يتم إنشاء عمود السلسلة المعقدة كسلاسل أصلية بطول يصلح ، سيتم إنشاء العمود المكافئ في الباندا كمصفوفة كائن بيثون. تؤدي المقارنة على حد سواء إلى دفع إنشاء مثيل DataFrame إلى سرعات غير متوقعة باستثناء إنشاء الفهرس المسؤول عن حوالي 92٪ من وقت إنشاء مثيل.

@ ARF1 إذا كنت تريد سرعات غير متوقعة ،

تقوم DataFrames بعمل أكثر بكثير من مجرد فكرة بسيطة حول كيفية وصف البيانات ومعالجتها. هذا ليس ما تفعله معهم في الواقع.

نسخ جميع عمليات الباندا تقريبًا. (كما هو الحال في معظم العمليات الخبيثة) ، لذلك لا تعرف ما الذي تبحث عنه.

jreback أنا

بالطبع سيكون البديل هو استخدام مصفوفة مختلفة من المصفوفات غير المتجانسة ولكن هذا يجعل التحليل مؤلمًا لأن التقطيع وما إلى ذلك لم يعد ممكنًا.

المواعيد المعتادة هي تقسيم حساباتك ، وتمرير أنواع فردية إلى سايثون.

هذا ما أفعله مع المتغير block_arrays في المثال الخاص بي.

أو مجرد الحصول على آلة أكبر.

عامل أسرع من 100+ يمثل تحديًا ماليًا قليلاً بالنسبة لي. ؛-)

@ ARF1 لديك نموذج غريب جدًا لكيفية عمل الأشياء. عادةً ما تقوم بإنشاء عدد صغير من إطارات البيانات ، ثم تعمل عليها. سرعة الخلق هي جزء صغير من أي حساب أو تلاعب حقيقي.

jreback : هذا ليس نموذجًا غريبًا. ربما يكون نموذجًا غريبًا إذا نظرت إلى الأشياء من منظور بيثون خالص. إذا كنت تعمل باستخدام كود C ++ ، فإن أسهل طريقة لقراءة البيانات في كائنات Python هي تمرير مؤشراتها إلى كائنات Python الموجودة مسبقًا. إذا كنت تفعل ذلك في سياق حساس للأداء ، فأنت تريد طريقة رخيصة ومستقرة (بمعنى موقع الذاكرة) لإنشاء كائن بيثون.

أنا بصراحة لست متأكدًا من سبب شيوع هذا الموقف في مجالس الباندا. أعتقد أنه أمر مؤسف ، بقدر ما أفهم أن الباندا هي بنية ذات مستوى أعلى من الباندا ، فإنه لا يزال من الأسهل على الناس تطوير "فوق" الباندا. يعد pandas DataFrame إلى حد بعيد أكثر الأنواع المرغوبة للعمل معه إذا كان لديك كود C يريد أن يبصق البيانات المجدولة في python ، لذلك يبدو هذا حقًا حالة استخدام مهمة.

من فضلك لا تأخذ ما أكتبه بشكل سلبي ، إذا لم أكن أعتقد أن pandas DataFrames كانت رائعة جدًا ، فسأستخدم فقط التسجيلات المكدسة أو شيء من هذا القبيل وأنجز ذلك.

@ ARF1 : في النهاية ، لا أتذكر الأسباب ، لكن أفضل ما استطعت فعله هو إنشاء DataFrame لكل نوع رقمي من مصفوفة numpy باستخدام Copy = False ، ثم استخدم pandas.concat مع Copy = False مرة أخرى لربطها. عندما تقوم بإنشاء نوع واحد DataFrame من مصفوفة عددية ، كن حذرًا جدًا بشأن اتجاه المصفوفة غير المعقدة. إذا كان الاتجاه خاطئًا ، فإن المصفوفات غير المتقاربة المقابلة لكل عمود ستكون غير مبسطة ، والباندا لا تحب ذلك وستقوم بعمل نسخة في أول فرصة. يمكنك التعامل مع Categoricals في النهاية ، حيث لا يتم دمجها ولا يجب تشغيل أي نسخ من بقية الإطار.

أوصي بكتابة بعض اختبارات الوحدة التي تؤدي هذه العملية خطوة بخطوة والاستيلاء باستمرار على المؤشرات إلى البيانات الأساسية (عبر واجهة الصفيف للمصفوفة الأساسية) والتحقق من أنها متطابقة للتأكد من حذف النسخة بالفعل. إنه قرار مؤسف للغاية من قبل الباندا أن نسخ / وضع المعلمات لا يجب تكريمها. أي ، حتى إذا قمت بتعيين copy = False لمُنشئ DataFrame ، فإن الباندا ستستمر في إجراء نسخة إذا قررت أنها بحاجة إلى ذلك من أجل إنشاء DataFrame. حقيقة أن الباندا يفعل ذلك بدلاً من رمي الحجج عندما لا يمكن تكريم الحجج يجعل كتابة التعليمات البرمجية الموثوقة التي تتجاهل النسخ مرهقة للغاية ، وتتطلب أن تكون منهجية للغاية. إذا لم تكتب اختبارات وحدة للتحقق ، فقد تقوم بتعديل شيء ما عن طريق الخطأ لاحقًا يتسبب في عمل نسخة ، وسيحدث ذلك بصمت ويفسد أدائك.

quicknir إذا قلت ذلك. أعتقد أنه يجب عليك ببساطة وضع ملف تعريف قبل محاولة تحسين الأمور. كما قلت من قبل ، وسوف يحدث مرة أخرى. يجب ألا يهيمن وقت البناء على أي شيء. إذا كان الأمر كذلك ، فأنت تستخدم فقط إطار البيانات للاحتفاظ بالأشياء ، فما الفائدة من استخدامه في المقام الأول؟ إذا لم تهيمن فما هي المشكلة؟

jreback تكتب ذلك ، بافتراض أنني لم

إذا كنت مستخدمًا نهائيًا لهذا الكود مع وضع عمليات محددة في الاعتبار ، فربما يكون ما تقوله عن البناء غير المسيطر صالحًا. في الواقع ، أنا مطور ، والمستخدمون النهائيون لهذا الكود هم أشخاص آخرون. لا أعرف بالضبط ما الذي سيفعلونه باستخدام DataFrame ، فإن DataFrame هي الطريقة الوحيدة للحصول على تمثيل في الذاكرة للبيانات الموجودة على القرص. إذا كانوا يريدون القيام بشيء بسيط للغاية باستخدام البيانات الموجودة على القرص ، فلا يزال يتعين عليهم الانتقال من خلال تنسيق DataFrame.

من الواضح أنني أستطيع دعم المزيد من الطرق للحصول على البيانات (مثل التركيبات غير المفككة) ، ولكن هذا سيزيد بشكل كبير من التفرع في الكود ، ويجعل الأمور أكثر صعوبة بالنسبة لي كمطور. إذا كان هناك سبب أساسي لضرورة أن تكون إطارات البيانات بطيئة جدًا ، فسأفهم ، وأقرر ما إذا كنت سأدعم DataFrame أو numpy أو كليهما. لكن لا يوجد سبب حقيقي لضرورة أن تكون بطيئة للغاية. يمكن للمرء كتابة عملية DataFrame.empty تأخذ مصفوفة من المجموعات حيث تحتوي كل مجموعة على اسم العمود ونوعه وعدد الصفوف.

هذا هو الفرق بين دعم المستخدمين وكتاب المكتبات. من الأسهل كتابة التعليمات البرمجية الخاصة بك بدلاً من كتابة مكتبة. ومن الأسهل أن تدعم مكتبتك المستخدمين فقط بدلاً من كتّاب المكتبات الآخرين. أعتقد فقط في هذه الحالة ، أن التخصيص الفارغ لإطارات البيانات سيكون بمثابة ثمار معلقة منخفضة في حيوانات الباندا مما سيجعل حياة الأشخاص مثلي و @ ARF1 أسهل.

حسنًا ، إذا كنت ترغب في الحصول على سولن موثق ومختبر بشكل معقول ، كل الأذنين. الباندا لديها عدد غير قليل من المستخدمين / المطورين. هذا هو السبب في أن DataFrame متعدد الاستخدامات وهو نفس السبب الذي يجعله يحتاج إلى الكثير من التحقق من الأخطاء والاستدلال. اهلا وسهلا بكم لمعرفة ما يمكنك القيام به كما هو موضح أعلاه.

أنا على استعداد لتخصيص بعض الوقت لتنفيذ هذا ، ولكن فقط إذا كان هناك بعض الإجماع المعقول على التصميم من عدد قليل من مطوري الباندا. إذا قمت بتقديم طلب سحب وكان هناك أشياء معينة يريد الناس تغييرها ، فهذا رائع. أو إذا أدركت بعد أن قضيت عشر ساعات في ذلك أنه لا توجد طريقة للقيام بشيء بطريقة نظيفة ، والطريقة الوحيدة للقيام بذلك قد تتضمن شيئًا يعتقد الناس أنه مرفوض ، فهذا رائع أيضًا. لكنني لست رائعًا حقًا في قضاء X من الساعات وإخباري أن هذا ليس مفيدًا ، فالتنفيذ فوضوي ، ولا نعتقد أنه يمكن تنظيفه حقًا ، أو يعقد قاعدة الشفرة ، وما إلى ذلك. لا أعرف ما إذا أنا بعيد عن هذا الشعور ، لم أقدم مساهمات كبيرة في مشروع برمجيات المصدر المفتوح من قبل لذلك لا أعرف كيف يعمل. إنه فقط في رسالتي الأولية بدأت باقتراح هذا الشيء بالذات ، وبعد ذلك بصراحة حصلت على انطباع منك أنه كان نوعًا ما "خارج نطاق" الباندا.

إذا كنت تريد ، يمكنني فتح مشكلة جديدة ، وإنشاء اقتراح تصميم محدد قدر الإمكان ، وبمجرد وجود تعليقات / موافقة مبدئية ، سأعمل على حلها عندما أتمكن من ذلك.

quicknir ، الشيء الرئيسي هو أنه يجب أن يجتاز مجموعة الاختبار بأكملها ، وهي شاملة جدًا.

هذا ليس خارج نطاق الباندا ، ولكن يجب أن تكون واجهة برمجة التطبيقات سهلة الاستخدام إلى حد ما.

لست متأكدًا من سبب عدم إعجابك

concat(list_of_arrays,axis=1,copy=False) أعتقد أن هذا يفعل ما تريده بالضبط (وإذا لم يكن كذلك ، فليس من الواضح ما الذي تريده بالفعل).

انتهى بي الأمر باستخدام أسلوب مشابه ، ولكن مع قائمة من إطارات البيانات التي تم إنشاؤها من مصفوفة واحدة ، كل نوع من أنواع مختلفة.

أولاً ، أعتقد أنني ما زلت واجهت بعض النسخ عندما فعلت هذه التقنية. كما قلت ، لا يحترم الباندا دائمًا copy = False ، لذلك من المرهق للغاية معرفة ما إذا كان رمزك يتم نسخه بالفعل أم لا. أتمنى حقًا أنه بالنسبة إلى pandas 17 ، قد يفكر المطورون في جعل copy = True الخيار الافتراضي ، ثم نسخ = رميات خاطئة عندما يتعذر التخلص من نسخة. لكن على أي حال.

ثانيًا ، كانت هناك مشكلة أخرى وهي الحاجة إلى إعادة ترتيب الأعمدة بعد ذلك. كان هذا محرجًا بشكل مدهش ، فالطريقة الوحيدة التي استطعت أن أجدها للقيام بذلك دون عمل نسخة كانت في الأصل جعل أسماء الأعمدة أعدادًا صحيحة بالترتيب النهائي المطلوب. ثم قمت بفرز فهرس في المكان. ثم قمت بتغيير أسماء الأعمدة.

ثالثًا ، وجدت أن النسخ كانت لا يمكن تجنبها لأنواع الطوابع الزمنية (تاريخ ووقت فارغ 64).

لقد كتبت هذا الرمز منذ فترة ، لذا فهو ليس جديدًا في ذهني. من المحتمل أنني ارتكبت أخطاء ، لكنني مررت بها بعناية شديدة وكانت تلك هي النتائج التي توصلت إليها في ذلك الوقت.

الكود الذي قدمته أعلاه لا يعمل حتى مع المصفوفات الفارغة. فشل مع: TypeError: لا يمكن أن تسلسل كائن غير NDFrame. عليك أن تجعلهم DataFrames أولاً.

لا يعني ذلك أنني لا أحب الحل الذي قدمته هنا أو أعلاه. أنا فقط لم أر حتى الآن واحدة بسيطة تعمل.

quicknir جيدا بلدي يعمل المثال أعلاه. يرجى تقديم ما تفعله بالضبط ويمكنني محاولة مساعدتك.

pd.concat ([np.zeros ((2،2))] ، المحور = 1 ، نسخة = خطأ)

أنا على pandas 0.15.2 ، لذلك ربما بدأ هذا العمل في 0.16؟

الرجاء قراءة سلسلة المستندات من pd.concat . تحتاج إلى تمرير DataFrame

راجع للشغل copy=True هو الافتراضي

صحيح ، هذا ما كتبته. مقتطف الشفرة الذي كتبته أعلاه يحتوي على list_of_arrays ، وليس list_of_dataframes. على أي حال ، أعتقد أننا نفهم بعضنا البعض. لقد انتهيت من استخدام طريقة pd.concat ، لكنها غير تافهة إلى حد ما ، فهناك مجموعة كاملة من مسكتك لرحلة الناس:

1) يجب عليك إنشاء قائمة بإطارات البيانات. يجب أن يحتوي كل إطار DataFrame على نوع dtype مميز واحد بالضبط. لذلك عليك أن تجمع كل أنواع dtypes المختلفة قبل أن تبدأ.

2) يجب إنشاء كل DataFrame من مصفوفة مفردة من النوع dtype المطلوب ، ونفس عدد الصفوف ، وعدد الأعمدة المطلوب ، والترتيب = علامة 'F' ؛ إذا كان الطلب = 'C' (افتراضيًا) ، فغالبًا ما تقوم الباندا بعمل نسخ عندما لا تفعل ذلك.

3) تجاهل 1) بالنسبة للفئات ، لا يتم دمجها في كتلة حتى تتمكن من معالجتها لاحقًا.

4) عند إنشاء كل أطر البيانات الفردية ، يجب تسمية الأعمدة باستخدام الأعداد الصحيحة التي تمثل الترتيب الذي تريدها به. وإلا فلن تكون هناك طريقة لتغيير ترتيب الأعمدة دون تشغيل النسخ.

5) بعد إنشاء قائمة DataFrames الخاصة بك ، استخدم concat. سيتعين عليك التحقق بشق الأنفس من أنك لم تفسد أي شيء ، لأن copy = False لن يتم التخلص منه إذا لم يكن بالإمكان حذف نسخة ، بل نسخها بصمت.

6) قم بفرز فهرس العمود لتحقيق الترتيب الذي تريده ، ثم أعد تسمية الأعمدة.

لقد طبقت هذا الإجراء بصرامة. إنها ليست مجرد خط واحد ، فهناك الكثير من الأماكن لارتكاب الأخطاء ، وأنا متأكد من أنها لا تزال لا تعمل مع الطوابع الزمنية ، وهناك الكثير من النفقات العامة غير الضرورية التي يمكن تجنبها من خلال عدم استخدام الواجهة فقط. إذا كنت ترغب في ذلك ، يمكنني كتابة مسودة لما تبدو عليه هذه الوظيفة باستخدام واجهة برمجة التطبيقات العامة فقط ، وربما يتم دمجها مع بعض الاختبارات لمعرفة ما إذا كانت بالفعل تستبعد النسخ ، ولأي أنواع.

أيضًا ، copy = False هو الإعداد الافتراضي لمُنشئ DataFrame على سبيل المثال. نقطتي الرئيسية هي أن الوظيفة التي لا تستطيع احترام حججها يجب أن ترمي بدلاً من "القيام بشيء معقول". أي ، إذا كان لا يمكن تكريم copy = False ، يجب طرح استثناء حتى يعرف المستخدم أنه يتعين عليه إما تغيير المدخلات الأخرى بحيث يمكن أن يتم حذف النسخة ، أو يجب عليهم تغيير النسخة إلى True. يجب ألا تحدث النسخ أبدًا بصمت عندما يكون copy = False ، فهذا أمر أكثر إثارة للدهشة وأقل تفضيلًا لمستخدم واعٍ بالأداء لاكتشاف الأخطاء.

لديك هنا الكثير من الخطوات التي ليست ضرورية
الرجاء إظهار مثال حقيقي كما فعلت أعلاه

أنت تدرك أن طريقة العرض غير المعقدة يمكنها إرجاع نسخة عن طريق عمليات إعادة تشكيل بسيطة جدًا (أحيانًا) وليس عمليات أخرى

لن يكون هناك سوى ضمان بسيط على النسخ لأنه ليس ممكنًا عادةً بدون الكثير من الاستبطان لضمان ذلك والذي بحكم تعريفه يتعارض مع الغرض من رمز الأداء القوي البسيط

يتوافق سلوك copy=False في إنشاء DataFrame مع وظيفة np.array لـ numpy's (على سبيل المثال ، إذا قمت بتوفير قائمة من المصفوفات ، فستعمل البيانات النهائية دائمًا على إنشاء نسخة).

يبدو أن هذه فجوة ميزة مؤسفة في الباندا. IMHO ، لن يكون لدينا حل مرضٍ مع النموذج الحالي لأجزاء الباندا الداخلية (التي تدمج الكتل). لسوء الحظ ، هذا ليس خيارًا حقًا للباندا ، لأن الباندا لا تزال بحاجة إلى العمل للأشخاص الذين يصنعون DataFrames مع الكثير من الأعمدة.

ما نحتاجه هو تطبيق DataFrame بديل مصمم خصيصًا للعمل مع بيانات مرتبة تخزن كل عمود بشكل مستقل كمصفوفات أحادية الأبعاد. هذا في الواقع مشابه إلى حد ما لنموذج البيانات في xray ، إلا أننا نسمح للأعمدة بأن تكون مصفوفات ذات أبعاد N.

أعتقد أن مُنشئ إطار بيانات الباندا العام عالي الأداء الذي يخصص مساحة فقط ليس تافهًا بالنظر إلى النطاق الواسع لأنواع الأعمدة المختلفة التي قد تحتاج إلى دعمها.

ومع ذلك ، يبدو الأمر واضحًا إلى حد ما بالنسبة لكتاب المكتبات الذين يرغبون في استخدام إطارات بيانات الباندا كحاوية بيانات عالية الأداء لتنفيذ مُنشئ إطار بيانات للتخصيص فقط يقتصر على أنواع الأعمدة التي يحتاجون إليها.

يمكن أن يكون مقطع الكود التالي بمثابة مصدر إلهام. يسمح بإنشاء مثيل لأطر البيانات غير المملوءة للتخصيص فقط بسرعة قريبة من السرعات الصغيرة. لاحظ أن الرمز يتطلب 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 ، والتي تغلف أنواع dtypes وأسماء الأعمدة وترتيب الأعمدة. على سبيل المثال ، يمكن تخزين أمر من نوع dtype إلى أرقام الأعمدة (للطلب) ، ومصفوفة منفصلة بكل الأسماء. من هذا التصميم ، يمكنك أن ترى أن التكرار البسيط والأنيق على الإملاء سيسمح بإنشاء مديري الكتلة بسرعة. يمكن بعد ذلك استخدام هذه الفئة في أماكن مثل مُنشئ فارغ ، أو في عمليات إعادة الفهرسة.

أعتقد أن مثل هذه الأفكار التجريدية ضرورية لبيانات أكثر تعقيدًا. بمعنى ما ، فإن DataFrame هو نوع بيانات مركب ، وسيحدد DataFrameLayout الطبيعة الدقيقة للتكوين.

بالمناسبة ، أعتقد أن شيئًا مشابهًا ضروريًا لـ Categoricals ؛ يجب أن يكون هناك تجريد من النوع CategoricalType يخزن الفئات ، سواء تم ترتيبها أم لا ، ونوع مصفوفة الدعم ، وما إلى ذلك ، أي كل شيء ما عدا البيانات الفعلية. في الواقع ، عندما تفكر في DataFrameLayout ، فإنك تدرك أن جميع الأعمدة يجب أن تحتوي على أنواع محددة تمامًا ، وهذا يمثل حاليًا مشكلة بالنسبة للفئات.

ما رأي الناس في هاتين الفئتين؟

quicknir لدينا بالفعل فئة CategoricalDtype - أوافق على إمكانية تمديدها لتشمل CategoricalType الذي وصفته بالكامل.

لست متأكدًا تمامًا من فئة DataFrameLayout . بشكل أساسي ، أعتقد أنه يمكننا استخدام نموذج بيانات بديل أبسط لإطارات البيانات (أكثر شبهاً بالطريقة التي يتم إجراؤها في R أو Julia). هناك بعض الاهتمام بهذا النوع من الأشياء وأظن أنه سيحدث في نهاية المطاف بشكل ما ، ولكن ربما ليس في أي وقت قريب (وربما ليس كجزء من مشروع الباندا).

quicknir yeh ، DataFrameLayout يعيد اختراع العجلة هنا. لدينا بالفعل مواصفات 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 هو نوع غير مكتمل. أحد الآثار المترتبة على ذلك هو أنني لا أرى كيف يمكن أن يمتد هذا لدعم Categoricals بشكل صحيح.

3) هذه الطريقة للتخزين الداخلي للمواصفات لا تتوافق بسهولة مع كيفية تخزين البيانات داخل DataFrame (أي في كتل من النوع dtype). يتضمن إنشاء الكتل من مثل هذه المواصفات الكثير من العمل الإضافي الذي يمكن إلغاؤه من خلال تخزين المواصفات بطريقة كما اقترحت ، مع إملاء من dtype إلى أرقام الأعمدة. عندما يكون لديك DataFrame به 2000 عمود ، فسيكون ذلك مكلفًا.

باختصار ، يعد النوع dtype لتمثيل السجل حلاً بديلاً لعدم وجود مواصفات مناسبة. يفتقر إلى العديد من الميزات الرئيسية وهو أضعف بكثير من حيث الأداء.

هناك العديد من المواضيع على SO تطلب هذه الميزة.

يبدو لي أن كل هذه المشكلات تنبع من قيام BlockManager بدمج أعمدة منفصلة في أجزاء ذاكرة واحدة ("الكتل").
لن يكون الحل الأسهل هو عدم دمج البيانات في كتل عند تحديد copy = False.

لدي BlockManager غير مدمج مصحح للقرد:
https://stackoverflow.com/questions/45943160/can-memmap-pandas-series-what-about-a-dataframe
التي اعتدت على حل هذه المشكلة.

هل كانت هذه الصفحة مفيدة؟
0 / 5 - 0 التقييمات