Numpy: مخفض الذرة (تراك # 236)

تم إنشاؤها على ١٩ أكتوبر ٢٠١٢  ·  49تعليقات  ·  مصدر: numpy/numpy

_التذكرة الأصلية http://projects.scipy.org/numpy/ticket/236 بتاريخ 2006-08-07 بواسطة مستخدم التتبع martin_wiechert ، المخصص إلى مجهول.

. تخفيض لا يتعامل مع المؤشرات المتكررة بشكل صحيح. عند تكرار الفهرس ، يجب إرجاع العنصر المحايد للعملية. في المثال أدناه [0 ، 10] ، وليس [1 ، 10] ، متوقع.

In [1]:import numpy

In [2]:numpy.version.version
Out[2]:'1.0b1'

In [3]:a = numpy.arange (5)

In [4]:numpy.add.reduceat (a, (1,1))
Out[4]:array([ 1, 10])
01 - Enhancement 23 - Wish List numpy.core

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

الدافع الرئيسي لـ reduceat هو تجنب تكرار حلقة تزيد عن reduce للحصول على أقصى سرعة. لذلك لست متأكدًا تمامًا من أن غلاف حلقة for يزيد عن reduce سيكون إضافة مفيدة جدًا لـ Numpy. سيتعارض مع الغرض الرئيسي reduceat .

علاوة على ذلك ، فإن منطق وجود reduceat و API ، كبديل متجه سريع للحلقة التي تزيد عن reduce ، هو منطق نظيف ومفيد. لن أتجاهلها ، بل أصلحها.

فيما يتعلق بالسرعة reduceat ، دعنا نفكر في مثال بسيط ، لكنه مشابه لبعض حالات العالم الحقيقي الموجودة في الكود الخاص بي ، حيث أستخدم reduceat :

n = 10000
arr = np.random.random(n)
inds = np.random.randint(0, n, n//10)
inds.sort()

%timeit out = np.add.reduceat(arr, inds)
10000 loops, best of 3: 42.1 µs per loop

%timeit out = piecewise_reduce(np.add, arr, inds)
100 loops, best of 3: 6.03 ms per loop

هذا فرق زمني يزيد عن 100 ضعف ويوضح أهمية الحفاظ على كفاءة reduceat .

باختصار ، سأعطي الأولوية لإصلاح reduceat على إدخال وظائف جديدة.

الحصول على start_indices و end_indices ، مفيد للغاية في بعض الحالات ، غالبًا ما يكون زائدًا عن الحاجة وأرى أنه إضافة محتملة ، ولكن ليس كإصلاح لـ reduceat غير المتسق سلوك.

ال 49 كومينتر

_ @ كتب

لسوء الحظ ، ربما تتبع طريقة التخفيض لـ NumPy سلوك طريقة التخفيض من Numeric لحالة الزاوية هذه.

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

[a [1]، add.reduce (a [1:])]

هذا طلب ميزة.

_trac user martin_wiechert كتب في 2006-08-08_

انظر أيضًا التذكرة رقم 835

تم تغيير الإنجاز إلى 1.1 بواسطة alberts في 2007-05-12

تم تغيير الإنجاز إلى Unscheduled بواسطة cournape في 2009-03-02

أعتقد أن هذا مرتبط ارتباطًا وثيقًا بـ # 835: إذا كان أحد المؤشرات len(a) ، فلن يتمكن reduceat إخراج العنصر في هذا الفهرس ، وهو أمر ضروري إذا ظهر الفهرس len(a) أو يتكرر في نهاية المؤشرات.

بعض الحلول:

  • خيار reduceat لعدم تعيين أي قيمة في المخرجات حيث end - start == 0
  • خيار لتعيين الإخراج إلى قيمة ثابتة معينة حيث end - start == 0
  • المعلمة where ، كما في ufunc() ، والتي تخفي المخرجات التي يجب حسابها على الإطلاق.

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

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

يجب أن يتصرف reduceat بشكل ثابت مع جميع المؤشرات. أي ، لكل فهرس i ، يجب أن يُرجع ufunc.reduceat(a, indices) ufunc.reduce(a[indices[i]:indices[i+1]]) .

يجب أن يكون هذا صحيحًا أيضًا بالنسبة للحالة indices[i] == indices[i+1] . لا أستطيع أن أرى أي سبب منطقي يفسر لماذا ، في هذه الحالة ، يجب أن يعيد خفض المستوى a[indices[i]] بدلاً من ufunc.reduce(a[indices[i]:indices[i+1]]) .

راجع أيضًا هنا تعليقًا مشابهًا لمبدع Pandas Wes McKinney .

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

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

ماذا تنوي لـ ufuncs بدون هوية ، مثل np.maximum؟

لمثل هذه الوظائف ، يجب أن يكون التخفيض الفارغ خطأ ، كما هو بالفعل
عند استخدام .reduce () بدلاً من .reduceat ().

في الواقع ، يجب أن يكون السلوك مدفوعًا بالاتساق مع ufunc.reduce(a[indices[i]:indices[i+1]]) ، وهو ما يتوقعه كل مستخدم. لذلك هذا لا يتطلب قرارات تصميم جديدة. يبدو حقًا وكأنه إصلاح خطأ طويل الأمد بالنسبة لي. ما لم يكن باستطاعة أي شخص تبرير السلوك غير المتسق الحالي.

njsmith أنا غير قادر على التسجيل في قائمة Numpy. لقد أرسلت عنواني هنا https://mail.scipy.org/mailman/listinfo/numpy-discussion لكنني لم أحصل على أي "بريد إلكتروني يطلب التأكيد". لست متأكدا ما إذا كان المرء بحاجة إلى متطلبات خاصة للاشتراك ...

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

إصدار reduceat المتوافق مع ufunc.reduce(a[indices[i]:indices[i+1]]) سيكون رائعًا حقًا. سيكون أكثر فائدة! إما وسيطة لتحديد السلوك أو دالة جديدة ( reduce_intervals ؟ reduce_segments ؟ ...؟) من شأنها تجنب كسر عدم التوافق مع الإصدارات السابقة.

ربما تميل إلى إهمال np.ufunc.reduceat allt معًا - يبدو أنه من المفيد أن تكون قادرًا على تحديد مجموعة من مؤشرات البداية والنهاية ، لتجنب الحالات التي يكون فيها indices[i] > indices[i+1] . أيضًا ، يشير الاسم at إلى تشابه أكبر بكثير مع at مما هو موجود حاليًا

ما أقترحه كبديل هو np.piecewise_reduce np.reducebins ، ربما نقي بيثون ، والذي يفعل في الأساس:

def reducebins(func, arr, start=None, stop=None, axis=-1, out=None):
    """
    Compute (in the 1d case) `out[i] = func.reduce(arr[start[i]:stop[i]])`

    If only `start` is specified, this computes the same reduce at `reduceat` did:

        `out[i]  = func.reduce(arr[start[i]:start[i+1]])`
        `out[-1] = func.reduce(arr[start[-1]:])`

    If only `stop` is specified, this computes:

        `out[0] = func.reduce(arr[:stop[0]])`
        `out[i] = func.reduce(arr[stop[i-1]:stop[i]])`

    """
    # convert to 1d arrays
    if start is not None:
        start = np.array(start, copy=False, ndmin=1, dtype=np.intp)
        assert start.ndim == 1
    if stop is not None:
        stop = np.array(stop, copy=False, ndmin=1, dtype=np.intp)
        assert stop.ndim == 1

    # default arguments that do useful things
    if start is None and stop is None:
        raise ValueError('At least one of start and stop must be specified')
    elif stop is None:
        # start only means reduce from one index to the next, and the last to the end
        stop = np.empty_like(start)
        stop[:-1] = start[1:]
        stop[-1] = arr.shape[axis]
    elif start is None:
        # stop only means reduce from the start to the first index, and one index to the next
        start = np.empty_like(stop)
        start[1:] = stop[:-1]
        start[0] = 0
    else:
        # TODO: possibly confusing?
        start, stop = np.broadcast_arrays(start, stop)

    # allocate output - not clear how to do this safely for subclasses
    if not out:
        sh = list(arr.shape)
        sh[axis] = len(stop)
        sh = tuple(sh)
        out = np.empty(shape=sh)

    # below assumes axis=0 for brevity here
    for i, (si, ei) in enumerate(zip(start, stop)):
        func.reduce(arr[si:ei,...], out=out[i, ...], axis=axis)
    return out

والتي لها خصائص جميلة:

  • np.add.reduce(arr) هو نفسه np.piecewise_reduce(np.add, arr, 0, len(arr))
  • np.add.reduceat(arr, inds) هو نفسه np.piecewise_reduce(np.add, arr, inds)
  • np.add.accumulate(arr) هو نفسه np.piecewise_reduce(np.add, arr, 0, np.arange(len(arr)))

الآن ، هل يريد هذا المرور عبر ماكينات __array_ufunc__ ؟ يجب أن تتم تغطية معظم ما يجب التعامل معه بالفعل بـ func.reduce - المشكلة الوحيدة هي السطر np.empty ، وهي مشكلة مشاركة np.concatenate .

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

اقترح Marten شيئًا مشابهًا لهذا في مناقشة مماثلة من ~ 1
قبل عام ، لكنه ذكر أيضًا إمكانية إضافة خيار "خطوة":

http://numpy-discussion.10968.n7.nabble.com/Behavior-of-reduceat-td42667.html

الأشياء التي أحبها (لذا +1 إذا كان أي شخص يحصي) من اقتراحك

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

الأشياء التي أعتقد أنها مهمة للتفكير مليًا في هذه الوظيفة الجديدة:

  • هل يجب أن نجعل "الخطوة" خيارًا؟ (اقول نعم)
  • هل يعقل بث مصفوفات المؤشرات ، أم يجب عليها ذلك
    يكون 1D؟
  • هل يجب أن تكون هذه دالة np أم طريقة ufunc؟ (أعتقد أنني أفضل ذلك
    كطريقة)

ومن قسم التخلص من الدراجات ، أفضل:

  • أعطه اسمًا لا يُنسى ، لكن ليس لدي اقتراح.
  • استخدم "ابدأ" و "توقف" (و "خطوة" إذا قررنا أن نبدأ بها)
    التناسق مع شريحة np.arange و Python.
  • إسقاط _indices من أسماء kwarg.

خايمي

الخميس، 13 أبريل 2017 في 13:47، اريك يزر [email protected]
كتب:

ربما أغري بإهمال np.ufunc.reduceat allt معًا - هو
يبدو أكثر فائدة أن تكون قادرًا على تحديد مجموعة من مؤشرات البداية والنهاية ، إلى
تجنب الحالات التي تكون فيها المؤشرات [i]> مؤشرات [i + 1]. كما يشير الاسم إلى أ
تشابه أكبر بكثير مما هو موجود في الواقع

ما أقترحه كبديل هو np.piecewise_reduce ، وهو في الأساس
هل:

def puewise_reduce (func ، arr ، start_indices = بلا ، end_indices = لا شيء ، المحور = -1 ، الخروج = لا شيء):
إذا كانت start_indices هي بلا و end_indices هي بلا:
start_indices = np.array ([0]، dtype = np.intp)
end_indices = np.array (arr.shape [محور] ، dtype = np.intp)
elif end_indices هي بلا:
end_indices = np.empty_like (start_indices)
end_indices [: - 1] = start_indices [1:]
end_indices [-1] = arr.shape [محور]
مؤشرات بداية elif هي بلا:
start_indices = np.empty_like (end_indices)
start_indices [1:] = end_indices
end_indices [0] = 0
آخر:
تأكيد لين (مؤشرات_بداية) == لين (مؤشرات نهاية)

if not out:
    sh = list(arr.shape)
    sh[axis] = len(end_indices)
    out = np.empty(shape=sh)

# below assumes axis=0 for brevity here
for i, (si, ei) in enumerate(zip(start_indices, end_indices)):
    func.reduce(arr[si:ei,...], out=alloc[i, ...], axis=axis)
return out

والتي لها خصائص جميلة:

  • np.ufunc.reduce هو نفسه np.piecewise_reduce (func، arr، 0،
    لين (أر))
  • np.ufunc.accumulate هو نفسه `np.piecewise_reduce (func، arr،
    np.zeros (len (arr)) ، np.arange (len (arr)))

الآن ، هل هذا يريد أن يمر عبر آلية _array_ufunc__؟ معظم
ما يجب التعامل معه يجب أن يتم تغطيته بالفعل بواسطة func.reduce - the
المشكلة الوحيدة هي خط np.empty ، وهي مشكلة تتسبب في np.concatenate
تشارك.

-
أنت تتلقى هذا لأنك مشترك في هذا الموضوع.
قم بالرد على هذا البريد الإلكتروني مباشرة ، وقم بعرضه على GitHub
https://github.com/numpy/numpy/issues/834#issuecomment-293867746 ، أو كتم الصوت
الخيط
https://github.com/notifications/unsubscribe-auth/ADMGdtjSCodONyu6gCpwofdBaJMCIKa-ks5rvgtrgaJpZM4ANcqc
.

-
(__ /)
(أوو)
(> <) Este es Conejo. Copia a Conejo en tu firma y ayúdale en sus planes
دي دوميناسيون مونديال.

استخدم "ابدأ" و "توقف"

منجز

هل يجب أن نجعل "الخطوة" خيارًا

يبدو أنه حالة استخدام ضيقة جدًا

هل يعقل بث مصفوفات المؤشرات ، أم يجب أن تكون 1D

محدث. من الواضح أن 1d سيئ ، لكن أعتقد أنه يجب علينا السماح بالبث 0d والبث ، لحالات مثل التراكم.

هل يجب أن تكون هذه دالة np أم طريقة ufunc؟ (أعتقد أنني أفضل ذلك
كطريقة)

كل طريقة ufunc هي شيء آخر للتعامل مع __array_ufunc__ .

الدافع الرئيسي لـ reduceat هو تجنب تكرار حلقة تزيد عن reduce للحصول على أقصى سرعة. لذلك لست متأكدًا تمامًا من أن غلاف حلقة for يزيد عن reduce سيكون إضافة مفيدة جدًا لـ Numpy. سيتعارض مع الغرض الرئيسي reduceat .

علاوة على ذلك ، فإن منطق وجود reduceat و API ، كبديل متجه سريع للحلقة التي تزيد عن reduce ، هو منطق نظيف ومفيد. لن أتجاهلها ، بل أصلحها.

فيما يتعلق بالسرعة reduceat ، دعنا نفكر في مثال بسيط ، لكنه مشابه لبعض حالات العالم الحقيقي الموجودة في الكود الخاص بي ، حيث أستخدم reduceat :

n = 10000
arr = np.random.random(n)
inds = np.random.randint(0, n, n//10)
inds.sort()

%timeit out = np.add.reduceat(arr, inds)
10000 loops, best of 3: 42.1 µs per loop

%timeit out = piecewise_reduce(np.add, arr, inds)
100 loops, best of 3: 6.03 ms per loop

هذا فرق زمني يزيد عن 100 ضعف ويوضح أهمية الحفاظ على كفاءة reduceat .

باختصار ، سأعطي الأولوية لإصلاح reduceat على إدخال وظائف جديدة.

الحصول على start_indices و end_indices ، مفيد للغاية في بعض الحالات ، غالبًا ما يكون زائدًا عن الحاجة وأرى أنه إضافة محتملة ، ولكن ليس كإصلاح لـ reduceat غير المتسق سلوك.

لا أعتقد أن السماح لمؤشرات البدء والإيقاف تأتي من مصفوفات مختلفة
سيحدث فرقًا كبيرًا في الكفاءة إذا تم تنفيذه في C.

في 13 أبريل 2017 الساعة 23:40 ، كتب divenex [email protected] :

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

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

فيما يتعلق بالسرعة المخفضة ، دعنا نفكر في مثال بسيط ، ولكنه مشابه لـ
بعض حالات العالم الحقيقي التي لدي في الكود الخاص بي ، حيث أستخدم تقليل:

ن = 10000
arr = np.random.random (n)
inds = np.random.randint (0، n، n // 10)
inds.sort ()
٪ timeit = np.add.reduceat (arr، inds) 10000 حلقة ، أفضل 3: 42.1 µs لكل حلقة
٪ timeit out = القطعة (np.add، arr، inds) 100 حلقة ، أفضل 3: 6.03 مللي ثانية لكل حلقة

هذا فارق زمني يزيد عن 100 ضعف ويوضح الأهمية
للحفاظ على الكفاءة المخفض.

باختصار ، أود إعطاء الأولوية لإصلاح المخفض على إدخال جديد
المهام.

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

-
أنت تتلقى هذا لأنك علقت.
قم بالرد على هذا البريد الإلكتروني مباشرة ، وقم بعرضه على GitHub
https://github.com/numpy/numpy/issues/834#issuecomment-293898215 ، أو كتم الصوت
الخيط
https://github.com/notifications/unsubscribe-auth/AAEz6xPex0fo2y_MqVHbNP5YNkJ0CBJrks5rviW-gaJpZM4ANcqc
.

هذا فارق زمني يزيد عن 100x ويوضح أهمية الحفاظ على كفاءة المخفض.

شكرًا على ذلك - أعتقد أنني قللت من تقدير النفقات العامة المرتبطة بالمرحلة الأولى من مكالمة reduce (يحدث هذا مرة واحدة فقط مقابل reduceat ).

ليست حجة ضد وظيفة حرة ، ولكنها بالتأكيد حجة ضد تنفيذها في لغة بيثون خالصة

ولكن ليس كإصلاح لسلوك التخفيض الحالي غير المتسق.

تكمن المشكلة في أنه من الصعب تغيير سلوك الكود الموجود منذ فترة طويلة.


امتداد آخر محتمل: عندما indices[i] > indices[j] ، احسب معكوسًا:

    for i, (si, ei) in enumerate(zip(start, stop)):
        if si >= ei:
            func.reduce(arr[si:ei,...], out=out[i, ...], axis=axis)
        else:
            func.reduce(arr[ei:si,...], out=out[i, ...], axis=axis)
            func.inverse(func.identity, out[i, ...], out=out[i, ...])

حيث np.add.inverse = np.subtract ، np.multiply.inverse = np.true_divide . ينتج عن هذا خاصية لطيفة

func.reduce(func.reduceat(x, inds_from_0)) == func.reduce(x))

فمثلا

a = [1, 2, 3, 4]
inds = [0, 3, 1]
result = np.add.reduceat(a, inds) # [6, -5, 9] == [(1 + 2 + 3), -(3 + 2), (2 + 3 + 4)]

تكمن المشكلة في أنه من الصعب تغيير سلوك الكود الموجود منذ فترة طويلة.

هذا جزئيًا هو السبب في أنني اقترحت في سلسلة رسائل البريد الإلكتروني إعطاء معنى خاص لمصفوفة ثنائية الأبعاد من المؤشرات يكون فيها البعد الإضافي 2 أو 3: ثم يتم تفسيرها (بشكل فعال) على أنها مجموعة من الشرائح. لكنني أدرك أن هذا أيضًا فوضوي إلى حد ما وبالطبع قد يكون لدى المرء طريقة reduce_by_slice أو slicereduce أو reduceslice .

ملاحظة: أعتقد أن أي شيء يعمل على العديد من ufuncs يجب أن يكون طريقة ، بحيث يمكن تمريرها من خلال __array_ufunc__ ويتم تجاوزها.

في الواقع ، اقتراح مختلف أعتقد أنه أفضل بكثير: بدلاً من إنقاذ reduceat ، لماذا لا تضيف وسيطة slice (أو start ، stop ، step ) إلى ufunc.reduce !؟ كما لاحظ @ eric-wieser ، فإن أي تطبيق من هذا القبيل يعني أنه يمكننا فقط إهمال reduceat تمامًا ، كما سيكون

add.reduce(array, slice=slice(indices[:-1], indices[1:])

(حيث نحن الآن أحرار في جعل السلوك يتطابق مع ما هو متوقع لشريحة فارغة)

هنا ، يمكن للمرء أن يبث الشريحة إذا كانت 0-d ، وقد يفكر في التمرير في مجموعات من الشرائح إذا تم استخدام مجموعة من المحاور.

تحرير: جعل ما ورد أعلاه slice(indices[:-1], indices[1:]) للسماح بالتمديد إلى مجموعة شرائح ( slice يمكن أن يحتفظ ببيانات عشوائية ، لذلك سيعمل هذا بشكل جيد).

ما زلت أجد إصلاحًا لـ reduceat ، لجعله نسخة موجهة 100٪ مناسبة من reduce ، حل التصميم الأكثر منطقية. بدلاً من ذلك ، لتجنب كسر الكود (لكن انظر أدناه) ، يمكن إنشاء طريقة مكافئة باسم reducebins ، وهي ببساطة نسخة مصححة من reduceat . في الواقع ، أنا أتفق مع @ eric-wieser على أن تسمية reduceat تنقل اتصالاً أكبر بالوظيفة at أكثر من تلك الموجودة.

أنا أفهم الحاجة إلى عدم كسر الكود. لكن يجب أن أقول إنني أجد صعوبة في تخيل أن الكثير من الكود يعتمد على السلوك القديم ، نظرًا لأنه ببساطة لم يكن له معنى منطقي ، وأود ببساطة أن أسميه خطأ طويل الأمد. أتوقع أن الكود باستخدام reduceat تأكد للتو من عدم تكرار indices ، لتجنب نتيجة هراء من reduceat ، أو إصلاح الناتج كما فعلت باستخدام out[:-1] *= np.diff(indices) > 0 . بالطبع سأكون مهتمًا بحالة مستخدم حيث تم استخدام السلوك / الخطأ القديم على النحو المنشود.

لست مقتنعًا تمامًا بحلmhvk slice لأنه يقدم استخدامًا غير قياسي للإنشاء slice . علاوة على ذلك ، فإنه سيكون غير متسق مع فكرة التصميم الحالية لـ reduce ، والتي تتمثل في _ "تقليل بُعد a بواحد ، من خلال تطبيق ufunc على طول محور واحد"

كما أنني لا أرى حالة مستخدم مقنعة لكل من مؤشري start و end . في الواقع ، أرى منطق التصميم الجميل للطريقة الحالية reduceat مشابهة من الناحية المفاهيمية لـ np.histogram ، حيث يتم استبدال bins ، والذي _ "يحدد حواف السلة ،" indices ، والتي تمثل أيضًا حواف الصناديق ، ولكن في مساحة الفهرس بدلاً من القيمة. ويطبق reduceat وظيفة على العناصر الموجودة داخل كل زوج من حواف الصناديق. المدرج التكراري هو بناء شائع للغاية ، لكنه لا يحتاج ، ولا يتضمن في Numpy ، خيارًا لتمرير متجهين للحواف اليمنى واليسرى. بالنسبة إلى نفس رياسون ، أشك في وجود حاجة قوية لكلا الحافتين في reduceat أو استبدالها.

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

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

أوافق أيضًا على أن الحل الأنظف هو تحديد طريقة جديدة مثل reducebins بواجهة برمجة تطبيقات ثابتة (وإيقاف reduceat ) ، وعدم محاولة الضغط عليها في reduce الذي يفعل شيئًا مختلفًا بالفعل.

مرحبا بالجميع ،

أريد أن أقضي على المناقشة القائلة بأن هذا خطأ. هذا سلوك موثق من docstring :

For i in ``range(len(indices))``, `reduceat` computes
``ufunc.reduce(a[indices[i]:indices[i+1]])``, which becomes the i-th
generalized "row" parallel to `axis` in the final result (i.e., in a
2-D array, for example, if `axis = 0`, it becomes the i-th row, but if
`axis = 1`, it becomes the i-th column).  There are three exceptions to this:

* when ``i = len(indices) - 1`` (so for the last index),
  ``indices[i+1] = a.shape[axis]``.
* if ``indices[i] >= indices[i + 1]``, the i-th generalized "row" is
  simply ``a[indices[i]]``.
* if ``indices[i] >= len(a)`` or ``indices[i] < 0``, an error is raised.

على هذا النحو ، أنا أعارض أي محاولة لتغيير سلوك reduceat .

يُظهر بحث جيثب السريع العديد والعديد من استخدامات الوظيفة. هل الجميع هنا متأكد من أنهم جميعًا يستخدمون مؤشرات متزايدة بشكل صارم فقط؟

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

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

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

shoyer يمكنني تخيل الحالات التي يتداخل فيها بعض np.reduceat أنه سيكون من الصعب أو المستحيل الحصول على حل أفضل باستخدام كود Python الخالص - غالبًا ما يكون مستخدمو الكود الوحيدون على استعداد لكتابته.

jni بالتأكيد ، قد يكون الاختزال إلى مجموعات ذات بدايات reduceat (والذي نريد بالتأكيد إهماله ، حتى لو لم نزله مطلقًا).

قد يكون التقليل إلى مجموعات مع بدايات وتوقفات عشوائية مفيدًا. لكن يبدو لي أنه زيادة كبيرة في النطاق

هذا يبدو تافها جدا بالنسبة لي. في الوقت الحالي ، لدينا رمز يعمل بشكل أساسي على ind1 = indices[i], ind2 = indices[i + 1] . إن تغيير ذلك لاستخدام مصفوفتين مختلفتين بدلاً من نفس الصفيف يجب أن يكون قليلًا جدًا من الجهد.

ويجب أن يكون سلوك التمرير الفردي عند اجتياز النطاقات المتجاورة بنفس السرعة تقريبًا كما هو الحال الآن - فالحركة العلوية الوحيدة هي وسيطة أخرى إلى nditer

هذا يبدو تافها جدا بالنسبة لي.

بالضبط. علاوة على ذلك ، إنها وظيفة يمتلكها المستخدمون بـ reduceat (باستخدام كل فهرس آخر) ، لكنها ستفقد مع وظيفة جديدة لا تدعم التداخل.

علاوة على ذلك ، يمكن لصيغة مؤلفة من مؤشرين محاكاة السلوك القديم (الغريب):

def reduceat(func, arr, inds):
    deprecation_warning()
    start = inds
    stops = zeros(inds.shape)
    stops[:-1] = start[1:]
    stops[-1] = len(arr)
    np.add(stops, 1, where=ends == starts, out=stops)  # reintroduce the "bug" that we would have to keep
    return reducebins(func, arr, starts, stops)

بمعنى أننا لسنا بحاجة إلى الحفاظ على تطبيقين متشابهين جدًا

أنا لا أعارض بشدة مؤشرات starts و stops لمؤشرات reducebins ، على الرغم من أنني ما زلت لا أرى مثالًا واضحًا حيث يلزم كلاهما. يبدو الأمر وكأنه تعميم np.histogram عن طريق إضافة حواف البداية والنهاية bins ...

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

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

لكن هذا يبدو مختلفًا تمامًا عن نطاق reducebins .

إذن ، ما هي حالة الاستخدام الطبيعية لـ starts و stops في reducebins ؟

إذن ، ما هي حالة الاستخدام الطبيعي للبداية والتوقف في الصناديق المختصرة؟

يمكن تحقيقه بوسائل أخرى ، ولكن المتوسط ​​المتحرك للطول k سيكون reducebins(np,add, arr, arange(n-k), k + arange(n-k)) . أظن أن تجاهل تكلفة تخصيص المؤشرات ، سيكون الأداء مشابهًا لمنهج as_strided .

بشكل فريد ، سيسمح reducebins بمتوسط ​​متحرك لمدة متفاوتة ، وهو أمر غير ممكن مع as_strided

حالة استخدام أخرى - توضيح بين تضمين النهاية أو البداية في صيغة الحجة الواحدة.

على سبيل المثال:

a = np.arange(10)
reducebins(np.add, start=[2, 4, 6]) == [2 + 3, 4 + 5, 6 + 7 + 8 + 9]  # what `reduceat` does
reducebins(np.add, stop=[2, 4, 6])  == [0 + 1, 2 + 3, 4 + 5]          # also useful

حالة استخدام أخرى - توضيح بين تضمين النهاية أو البداية في صيغة الحجة الواحدة.

أنا لا أفهم هذا تماما. هل يمكنك تضمين موتر الإدخال هنا؟ أيضًا: ما هي القيم الافتراضية لـ start / stop ؟

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

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

In [2]: A = np.random.random((4000, 4000))
In [3]: B = sparse.csr_matrix((A > 0.8) * A)
In [9]: %timeit np.add.reduceat(B.data, B.indptr[:-1]) * (np.diff(B.indptr) > 1)
1000 loops, best of 3: 1.81 ms per loop
In [12]: %timeit B.sum(axis=1).A
100 loops, best of 3: 1.95 ms per loop
In [16]: %timeit np.maximum.reduceat(B.data, B.indptr[:-1]) * (np.diff(B.indptr) > 0)
1000 loops, best of 3: 1.8 ms per loop
In [20]: %timeit B.max(axis=1).A
100 loops, best of 3: 2.12 ms per loop

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

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

أنا مع shoyer تمامًا بشأن تعليقه الأخير.

لنقم ببساطة بتعريف out=ufunc.reducebins(a, inds) أنه out[i]=ufunc.reduce(a[inds[i]:inds[i+1]]) لجميع i لكن الأخير ، وإهمال reduceat .

تبدو حالات الاستخدام الحالية للمؤشرات starts و ends بشكل طبيعي ومن المحتمل أن يتم تنفيذها بشكل أكثر كفاءة مع وظائف بديلة مثل as_strided أو التلافيف.

@ shoyer :

أنا لا أفهم هذا تماما. هل يمكنك تضمين موتر الإدخال هنا؟ أيضًا: ما هي القيم الافتراضية لبدء / إيقاف؟

تحديث مع المدخلات. شاهد تنفيذ reduce_bins في التعليق الذي بدأ هذا للقيم الافتراضية. لقد أضفت docstring هناك أيضًا. هذا التنفيذ مكتمل الميزات ولكنه بطيء (بسبب كونه بيثون).

ولكن هذا (قليلاً) يكون أصعب عندما تبدو الواجهة مختلفة.

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

np.add.reduce_at(arr, inds)
reduce_bins(np.add, arr, inds)
reduce_bins(np.add, arr, start=inds)

(تمييز الطريقة / الوظيفة ليس شيئًا أهتم به كثيرًا ، ولا يمكنني تحديد طريقة ufunc جديدة كنموذج أولي في Python!)


jni :

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

أنت مخطئ ، إنه كذلك - تمامًا مثل ufunc.reduceat بالفعل. من الممكن أيضًا ببساطة عن طريق تمرير start[i] == end[i] .

يمكن حل لغز التسلسل الفارغ ... عن طريق توفير قيمة أولية.

نعم ، لقد غطينا هذا بالفعل ، و ufunc.reduce يقوم بذلك بالفعل عن طريق ملء ufunc.identity . ليس من الصعب إضافة هذا إلى ufunc.reduecat ، خاصة إذا تم دمج # 8952. ولكن كما قلت بنفسك ، فإن السلوك الحالي موثق ، لذا لا ينبغي لنا على الأرجح تغييره.


تضمين التغريدة

لنقم ببساطة بتعريف out = ufunc.reducebins (a، inds) كـ out [i] = ufunc.reduce (a [inds [i]: inds [i + 1]]) لجميع i ما عدا الأخير

إذن len(out) == len(inds) - 1 ؟ هذا يختلف عن السلوك الحالي لـ reduceat ، لذا فإن حجة


الكل: لقد قمت بالاطلاع على التعليقات السابقة وأزلت ردود البريد الإلكتروني المقتبسة ، لأنها كانت تجعل قراءة هذه المناقشة صعبة

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

تجاهل مخاوف التوافق ، يجب أن يكون الناتج reducebins (في 1D) بحجم inds.size-1 ، لنفس السبب الذي يجعل حجم np.diff(a) a.size-1 و np.histogram(a, bins) هو bins.size-1 . ومع ذلك ، فإن هذا يتعارض مع الرغبة في الحصول على بديل بدون حجز مقابل reduceat .

لا أعتقد أن هناك حجة مقنعة مفادها أن الجواب الصحيح هو a.size-1 - بما في ذلك المؤشر 0 و / أو المؤشر n يبدو سلوكًا معقولًا أيضًا. يبدو كل منهم في متناول اليد في بعض الظروف ، لكنني أعتقد أنه من المهم جدًا أن يكون هناك بديل.

هناك أيضًا حجة أخرى لـ stop / start للاختباء هنا - فهي تسمح لك ببناء سلوك يشبه diff إذا كنت تريد ذلك ، بتكلفة قليلة جدًا ، مع الحفاظ على السلوك reduceat :

a = np.arange(10)
inds = [2, 4, 6]
reduce_bins(a, start=inds[:-1], stop=inds[1:])  #  [2 + 3, 4 + 5]

# or less efficiently:
reduce_at(a, inds)[:-1}
reduce_bins(a, start=inds)[:-1]
reduce_bins(a, stop=inds)[1:]

@ eric-wieser سأكون على ما يرام مع الوسيطات المطلوبة start و stop ، لكني لا أحب جعل إحداها اختيارية. ليس من الواضح أن تقديم start يعني out[i] = func.reduce(arr[start[i]:start[i+1]]) بدلاً من out[i] = func.reduce(arr[start[i]:]) ، وهو ما كنت أتوقعه.

واجهة برمجة التطبيقات المفضلة لدي لـ reducebins مثل reduceat لكن بدون "الاستثناءات" المربكة المذكورة في docstring . فقط:

بالنسبة إلى i in range(len(indices)) ، يحسب الاختزال ufunc.reduce(a[indices[i]:indices[i+1]]) ، والذي يصبح "الصف" العام رقم i الموازي للمحور في النتيجة النهائية (على سبيل المثال ، في مصفوفة ثنائية الأبعاد ، على سبيل المثال ، إذا كان المحور = 0 ، يصبح الصف الأول ، ولكن إذا كان المحور = 1 ، فإنه يصبح العمود الأول).

يمكنني أن أذهب في كلتا الحالتين إلى "الاستثناء" الثالث الذي يتطلب مؤشرات غير سلبية ( 0 <= indices[i] <= a.shape[axis] ) ، والتي أراها بمثابة فحص للعقل أكثر من كونها استثناء. ولكن ربما يمكن أن يذهب هذا أيضًا - يمكنني أن أرى كيف يمكن أن تكون المؤشرات السلبية مفيدة لشخص ما ، وليس من الصعب القيام بالحسابات لتطبيع مثل هذه المؤشرات.

عدم إضافة فهرس تلقائيًا في النهاية يعني أن النتيجة يجب أن تكون بطول len(a)-1 ، مثل نتيجة np.histogram .

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

ليس من الواضح أن تقديم البداية فقط يعني الخروج [i] = func.reduce (arr [start [i]: start [i + 1]]) بدلاً من الخروج [i] = func.reduce (arr [start [i] :]) ، وهو ما كنت أتوقعه.

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

التي تتطلب مؤشرات غير سالبة (0 <= مؤشرات [i] <a. شكل [محور])

لاحظ أن هناك أيضًا خطأ هنا (# 835) - يجب أن يكون الحد الأعلى شاملاً ، لأن هذه شرائح.

لاحظ أن هناك أيضًا خطأ هنا - يجب أن يكون الحد العلوي شاملاً ، لأن هذه شرائح.

ثابت ، شكرا.

ليس في الدالة reduceat نفسها ، فأنت لا ؛)

تبين أن :\doc\neps\groupby_additions.rst يحتوي على اقتراح (IMO أدنى) لوظيفة reduceby .

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