Ipython: لا يعمل٪ matplotlib المضمن بشكل جيد مع تفاعل ipywidgets 6.0

تم إنشاؤها على ٣ مارس ٢٠١٧  ·  46تعليقات  ·  مصدر: ipython/ipython

يبدو أنه في دفتر Jupyter ، لم يعد يلعب بشكل جيد مع ipywidgets الجديد 6.0 @interact %matplotlib inline في دفتر jupyter. أفضل ما يمكنني قوله ، تنتظر الواجهة الخلفية المضمنة حتى تنتهي الخلية تمامًا قبل إرسال رسالة display_data مع الصورة (انظر خطاف ما بعد التنفيذ المسجل على https://github.com/ipython/ipython/blob/7cde22957303ab53df8bd464ad5d7ed616197f31/ IPython / core / pylabtools.py # L383). في ipywidgets 6.0 ، قمنا بتغيير كيفية عمل التفاعلات لتكون أكثر تحديدًا حول المخرجات التي نلتقطها - نحن نلتقط رسائل الإخراج المرسلة أثناء تنفيذ الوظيفة ، ونعرض تلك الموجودة في الإخراج التفاعلي. ومع ذلك ، نظرًا لأن الواجهة الخلفية المضمنة matplotlib ترسل صورتها بعد تنفيذ الوظيفة ، فإننا نحصل فقط على سلسلة ضخمة من الصور في منطقة الإخراج للخلية.

import matplotlib.pyplot as plt
from ipywidgets import interact

%matplotlib inline
x = [1,2,5,4,3]
@interact(n=(2,5))
def f(n):
    plt.plot(x[:n])

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

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

import matplotlib.pyplot as plt
from ipywidgets import interact

x = [1,2,5,4,3]
@interact(n=(2,5))
def f(n):
    plt.plot(x[:n])
    plt.show()

CC @ سيلفان كورلاي ، تاكاسويل.

التعليق الأكثر فائدة

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

ال 46 كومينتر

يبدو أن هذا يعمل ، ولكن هل لدى أي شخص حل لأفضل طريقة؟

import matplotlib.pyplot as plt
from ipywidgets import interact
from ipykernel.pylab.backend_inline import flush_figures

%matplotlib inline
x = [1,2,5,4,3]
@interact(n=(2,5))
def f(n):
    plt.plot(x[:n])
    flush_figures()

CCminrk ، الذي عمل على الواجهة الخلفية mpl المضمنة.

لا أعتقد أن هناك الكثير يمكننا القيام به.

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

غلاف خاص @interact للتدفق بعد كل عملية؟

غلاف خاص interact للتدفق بعد كل عملية؟

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

تحرير: للتلخيص ، أعتقد أنني أوافق على أنه لا يوجد الكثير مما يمكننا (أو ينبغي) القيام به ...

اتضح أن هذا أسهل بكثير مما كنت أعتقد - لابد أنني ارتكبت خطأً عندما حاولت اختبار هذا سابقًا. لا نحتاج إلى استدعاء flush_figures ، يكفي فقط استدعاء عرض plt.show القياسي:

import matplotlib.pyplot as plt
from ipywidgets import interact

%matplotlib inline
x = [1,2,5,4,3]
@interact(n=(2,5))
def f(n):
    plt.plot(x[:n])
    plt.show()

أعتقد أنه من المعقول تمامًا أن نطلب من الأشخاص إدخال أمر show () في تفاعلهم ، لذلك أقوم بإغلاق هذا.

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

أتوقع أن يفاجأ الناس بجميع أمثلة التفاعل مع matplotlib للتوقف عن عرض الأرقام ، نظرًا لأن matplotlib "التفاعلي" يعني عمومًا عرض plt.ow غير ضروري. ربما لا ينبغي أن يؤدي وضعها في وظيفة تفاعلية إلى تغيير ذلك.

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

لا أعتقد أننا يجب أن ننفذ جميع معالجات ما بعد التنفيذ تلقائيًا (من يعرف ماذا سيفعلون - ربما الكثير من الأشياء!). ماذا عن وجود خطاف flush_display ؟ سنقوم بمسحها قبل بدء التفاعل ، ثم نظفها مرة أخرى قبل إنهائها. وبهذه الطريقة يكون الأمر مشابهًا تمامًا لمسح stdout / stdin.

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

جارٍ إعادة الفتح ، نظرًا لأن المحادثة لم تُغلق بعد ...

تعجبني فكرة خطاف flush_display ، وستعمل من الناحية النظرية ، على الرغم من أنها تتطلب إصدارات منسقة من ipython و ipykernel و ipywidgets لتحديد خطاف جديد وتبديل الواجهة الخلفية لاستخدامه.

إذا فكرنا في تفاعل يتصرف حقًا كخلية واحدة خاصة به

أعتقد أن هذا هو المفتاح ، وهو "إذا" كبير. هو كيف أفكر في ذلك، ولكن أنا لست الجميع (حتى الآن).

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

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

سؤال إضافي: هل سيعمل الإخراج المتطابق في JLab؟

تعمل عناصر واجهة

SylvainCorlay أعلم أنهم يعملون الآن ، والناس في آن أربور يحبونها. كان سؤالي هو أنه سينجح إذا تم إجراء التغييرات على النحو الذي تمت مناقشته أعلاه.

مسكتك آسف للضجيج.

على المدى الطويل ، أعتقد أن النهج الذي يجب أن يعمل بشكل أفضل هو الواجهة الخلفية لـ ipympl widgets إلى matplotlib.

ومع ذلك ، سنحتاج إلى جعل كل هذا أكثر سهولة مع conf.d وما إلى ذلك ...

نعم ، يجب أن تعمل المخرجات المتطابقة مع التغييرات التي تمت مناقشتها أعلاه. شكرا للتأكد!

الشيء المربك في هذا هو أنه إذا كان لدينا خلية بها رمز ، ثم تشغيل تفاعلي ، ثم المزيد من التعليمات البرمجية ، فسيتم تنفيذ الخلية فعليًا مثل ثلاث خلايا (أي ، تشغيل ما بعد التنفيذ ثلاث مرات مختلفة).

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

لا أعتقد أنه سيكون لديك أكثر من واحد إذا استخدمت @interact_manual ، على سبيل المثال (لست متأكدًا).

ألن تكون مرتين ، رغم ذلك؟ واحد للخلية ككل والآخر للطلب الأول للتفاعل؟

ضع في اعتبارك هذا الرمز:

plt.plot([1,2,3])
@interact(n=(1,10))
def f(n):
    plt.plot([1,n])
plt.plot([4,5,6])

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

في الواقع ، لست متأكدًا مما يتوقعه المستخدم في المثال أعلاه. ماذا تتوقع؟

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

هل هناك أي سبب لعدم استدعاء flush_figures بعد كل .plot() ؟

myyc إنه تغيير دلالي مهم في السلوك. يضيف plot عناصر على قطعة الأرض ....

قصدته في السلوك المضمن ipython المحدد ، على عكس matplotlib.

تحرير : أنا متأكد من أنها حالة استخدام أكثر شيوعًا أن يكون هناك تفاعل واحد لكل خلية مع ذلك باعتباره "وظيفة التخطيط الوحيدة" ، على عكس الحالة الموضحة أعلاه ...

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

من المحتمل أن يكون النهج المناسب في المستقبل هو استخدام الواجهة الخلفية للكمبيوتر الدفتري أو ipympl وتحرير الشكل.

لا يبدو تنفيذ الحلول الخاصة بـ matplotlib للواجهة الخلفية المضمنة في تفاعل ipywidgets صحيحًا.

في الكود الذي قدمه جيسون:

plt.plot([1,2,3])
@interact(n=(1,10))
def f(n):
    plt.plot([1,n])
plt.plot([4,5,6])

الناتج المتوقع هو رقم به الكثير من الخطوط ..

نعم أوافق تمامًا. لقد أصلحتها مؤقتًا بهذه الطريقة (تجاوزت طرق رسم الباندا) في الإعداد المحلي الخاص بي ، "في انتظار الإصلاح" :) تجاهل ذلك ، فإنه يقدم مجموعة من الآثار الجانبية الفظيعة.

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

تحرير : لماذا لا تكتبها بهذه الطريقة إذن ، بطريقة أكثر وضوحا؟

@interact(n=(1,10))
def f(n):
    plt.plot([1,2,3])
    plt.plot([1,n])
    plt.plot([4,5,6])

يمكنك التحقق من حزمة ipympl (https://github.com/matplotlib/jupyter-matplotlib) ، وهي أداة matplotlib. لا يزال ipympl في مرحلة مبكرة جدًا ، ولكن يجب أن يكون لديه جدول إصدار أسرع من matplotlib core ، ودعم سابق لـ jupyterlab وما إلى ذلك ...

حسنًا ، تبين أن التجاوز فكرة سيئة. سوف أتطلع إلى إصلاح المنبع :)

فقط للمساهمة بوجهة نظر مستخدم غير متعلم: بعد التحديث إلى ipywidgets 6.0 ، وجدت أن الكثير من كود عنصر واجهة المستخدم الخاص بي لم يعمل (بسبب المشكلة التي تمت مناقشتها هنا). تمكنت من إصلاحه عن طريق إضافة مكالمات إلى plt.show() ، لكنني وجدت أيضًا أنه يجب أن أكون حذرًا للغاية بشأن المكان الذي أضيف فيه مثل هذه المكالمات ؛ يبدو أن استدعاء هذا في بعض الأحيان في وقت مبكر جدًا يؤدي إلى عدم ظهور المخططات اللاحقة (حتى إذا تم استدعاء plt.show() مرة أخرى). لم أقم بمطاردة هذا بما يكفي لإنشاء مثال بسيط ، لكن السلوك الحالي يبدو لي غير بديهي - أنا أبرمج عن طريق التجربة والخطأ للالتفاف حوله.

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

يتم استدعاء خطافات ما بعد التنفيذ (أنا متأكد تمامًا) - يمكنك رؤية ذلك من خلال:

import matplotlib.pyplot as plt
from ipywidgets import interact
%matplotlib inline

@interact(n=(0,10))
def f(n):
    plt.plot([0,n])

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

import matplotlib.pyplot as plt
from ipywidgets import interact, IntSlider
%matplotlib inline

x = IntSlider()
@interact(n=x)
def f(n):
    plt.plot([0,n])
    plt.show()
@interact(n=x)
def f(n):
    plt.plot([0,n,0,n])
    plt.show()

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

يجب أن يكون لدينا طريقة أدق لتنظيف المخرجات الناتجة في كتلة من التعليمات البرمجية

بالنسبة لتخطيط matplotlib ، يحدث هذا التفريغ عند إجراء plt.show

يجب أن أكون حذرًا جدًا بشأن المكان الذي أضيف فيه مثل هذه المكالمات ؛ يبدو أن استدعاء هذا في بعض الأحيان في وقت مبكر جدًا يؤدي إلى عدم ظهور المؤامرات اللاحقة

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

يجب أن أكون حذرًا جدًا بشأن المكان الذي أضيف فيه مثل هذه المكالمات ؛ يبدو أن استدعاء هذا في بعض الأحيان في وقت مبكر جدًا يؤدي إلى عدم ظهور المؤامرات اللاحقة

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

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

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

jasongrout بعد مزيد من البحث ، أعتقد أن ما كنت أواجهه هو خاصية دفتر Jupyter نفسه ، وليس من عناصر واجهة المستخدم (ولكنه يتفاعل مع الأدوات الآن لأننا مضطرون إلى الاتصال بـ plt.show() ).

إذا قام أحدهم بإنشاء قطعة أرض في خلية دفتر ملاحظات واستدعى plt.show() ، فلن تظهر التعديلات اللاحقة على الشكل. إليك مثال بسيط:

%matplotlib inline
import matplotlib.pyplot as plt

def plotfun(t):
    fig, ax = plt.subplots(1,1)
    ax.plot([0,t],[0,t])
    plt.show()
    ax.plot([t,0],[0,t])
    plt.show()

plotfun(2)

يظهر مؤامرة السطر الأول فقط.

هذا مثال أبسط. خليتان ، بدون الواجهة الخلفية %matplotlib inline . ربما يمكن أن يلقي tacaswell بعض الضوء على سبب عدم ظهور قطعة الأرض الثانية من plt.show .

import matplotlib.pyplot as plt
fig, ax = plt.subplots(1,1)
ax.plot([0,1],[0,1])
plt.show()

يظهر المؤامرة

ax.plot([1,0],[0,1])
plt.show()

لا تظهر أي شيء

تم التأكيد على جهازي باستخدام أحدث مستقر jupyter و ipython و ipywidgets و matplotlib و python 3.

تم التأكيد هنا أيضًا.

jasongrout هذا هو السلوك المتوقع لأنه في الخلية الأولى تقوم بإنشاء شكل ( تعرضه . في الخلية الثانية ، يمكنك بعد ذلك إضافة سطر إلى المحاور الموجودة (في الشكل الحالي). مع %matplotlib qt أو ipympl (أو أي من واجهات المستخدم الرسومية الكاملة) ، يجب تحديث المؤامرة مع السطر الثاني. لا أتوقع أن يعمل هذا مع مضمّن لأنك تحصل على لقطة واحدة فقط لتحديثه (وهذا هو السبب في أنني لست من المعجبين بـ Inline ، فهو يطرح _جميع_ الوظائف التفاعلية).

tacaswell هل هذا السلوك المضمَّن منطقيًا أيضًا للمثال السابق الذي قدمته ، والذي هو الكل في خلية واحدة؟ ها هو مرة أخرى:

%matplotlib inline
import matplotlib.pyplot as plt

def plotfun(t):
    fig, ax = plt.subplots(1,1)
    ax.plot([0,t],[0,t])
    plt.show()
    ax.plot([t,0],[0,t])
    plt.show()

plotfun(2)

أعتقد أنه في plt.show تعرض الواجهة الخلفية المضمنة نفسها إلى png ثم تقوم بإلغاء تسجيل الرقم من pyplot بحيث لا يتم عرضه مرة أخرى في العرض التالي ، حتى لو كان في نفس الخلية ، ولكن يجب أن تعمل الاستدعاءات المتعددة لأساليب المحاور بدون show s الإضافية.

فقط للتوضيح ، إليك رمز الواجهة الخلفية المضمّن: https://github.com/ipython/ipykernel/blob/master/ipykernel/pylab/backend_inline.py

هل هناك إصدار من ipywidgets و / أو matplotlib يمكنني الرجوع إليه لجعل أدواتي التفاعلية تعمل مرة أخرى؟

jasongrout يبدو من الكود كما لو أنني قد أرغب في تعيين InlineBackend.close_figures إلى False . لكنني لست متأكدًا من كيفية تعيين هذه الخاصية - هل أحتاج إلى استخدام نوع من السحر؟

(هذا السؤال يبدو مضحكا لكنني أعنيه حرفيا)

أثار https://github.com/jupyter-widgets/ipywidgets/issues/1457 هذه المشكلة مرة أخرى. ellisonbg - ماذا لو طبقنا حل flush_figures الذي أشرت إليه أعلاه - استدعاء flush_figures قبل وبعد عمليات التشغيل التفاعلية ، لطرد أي مؤامرات قبل التفاعل ، وطرد أي شيء تم رسمه أثناء التفاعل؟ أعتقد أننا قد نضطر إلى التحقق لمعرفة ما إذا كنا نعمل في الواجهة الخلفية mpl المضمنة.

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

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

مع plt.show() أو flush_figures() ، إذا تم تحديث الكمبيوتر الدفتري (بدون إعادة تشغيل النواة) ، فإن الرقم يختفي حتى مع Save Notebook with Widgets. بدون عوامل التخفيف هذه ، ليس هذا هو الحال على الرغم من أنه لا يزال هناك مشكلة مخرجات مؤامرة متعددة. هل هذا مقصود؟ ينطبق الأمر نفسه إذا تم إيقاف تشغيل النواة ، بينما في الإصدارات الأقدم من ipywidgets ، سيتم حفظ الحالة النهائية للشكل في دفتر الملاحظات على غرار مؤامرة غير تفاعلية ، والتي كانت سهلة الاستخدام.

آسف إذا كان هذا خارج النطاق ، أو إذا كنت أفعل شيئًا خاطئًا. تشغيل ipywidgets 6.0.0 و matplotlib 2.0.2 و Notebook 5.0.0 و python 3.6.1.

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