Numpy: أول عنصر غير صفري (تتبع # 1673)

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

_Original تذكرة http://projects.scipy.org/numpy/ticket/1673 بتاريخ 2010-11-13 بواسطة trac user tom3118 ، مخصصة لـ unknown.

يقترح "numpy لمستخدمي matlab" استخدام
nonzero(A)[0][0]
للعثور على فهرس العنصر الأول غير الصفري للمصفوفة A.

تكمن المشكلة في أن A قد يكون طوله مليون عنصر وأن العنصر الأول قد يكون صفرًا.

هذه عملية شائعة للغاية. ستكون الطريقة الفعالة المضمنة لهذا الأمر مفيدة للغاية. كما أنه سيسهل انتقال الأشخاص من Matlab حيث يكون find شائعًا جدًا.

01 - Enhancement Other

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

أعلم أن هذا قد تأخر 3 سنوات ، ولكن هل هذا مشمول في numpy الآن؟ قادمة من خلفية Matlab تبدو هذه الوظائف مهمة حقًا بالنسبة لي. العلاقات العامة ستكون محل تقدير كبير (ليس هذا أنا أحد المطورين).

ال 26 كومينتر

_trac user tom3118 كتب بتاريخ 2010-11-13_

حالة الاستخدام ذات الصلة هي:
filter(test,A)[0]
فيها إما A طويلة أو test غالية.

_ @ rgommers كتب بتاريخ 2011-03-24_

ليس من الضروري أن تكون أول قيمة غير صفرية فقط ، أولًا أي قيمة ستكون مفيدة.

_ @ rgommers كتب بتاريخ 2011-03-24_

كما هو مذكور في # 2333 ، المعنى لا لبس فيه لـ 1-D. بالنسبة إلى دلالات> 1-D ، فهي مطروحة للنقاش.

ربما تعمل الكلمة الأساسية التي تحدد ترتيب التكرار على المحاور. أو يمكن ببساطة أن تكون غير محددة لـ> 1-D.

_trac المستخدم lcampagn كتب بتاريخ 2011-07-09_

لقد رأيت العديد من الطلبات لـ find_first في numpy ، لكن معظم هذه الطلبات لها متطلبات مختلفة (وغير متوافقة) بشكل دقيق مثل "العثور على القيمة الأولى أقل من x" أو "العثور على القيمة الأولى غير الصفرية". أقترح مواصفات الوظيفة التالية:

  ind = array.find(x, testOp='eq', arrayOp='all', axis=0, test=None)
  arguments:
    x       -> value to search for
    testOp  -> condition to test for ('eq', 'ne', 'gt', 'lt', 'ge', 'le')
    arrayOp -> method for joining multiple comparisons ('any' or 'all')
    axis    -> the axis over which to search
    test    -> for convenience, this may specify a function to call to perform
               the test. This is not expected to be efficient.
  returns: 
    first index where condition is true (or test returns true, if given)
    or None if the condition was never met

إذا كانت المصفوفة تحتوي على ndim> 1 ، فسيتم إجراء الاختبارات باستخدام قواعد البث العادية.
على سبيل المثال ، إذا كان لدي مصفوفة بالشكل (2،3) ، فسيكون ما يلي صالحًا:

  ## find first row with all values=0
  array.find(0, testOp='eq', arrayOp='all', axis=0)
  ## equivalent to:
  for i in range(array.shape[axis]):
    if (array[i] == 0).all():
      return i

  ## find first column with any element greater than its corresponding element in col
  col = array([1,2])
  array.find(col, testOp='gt', arrayOp='any', axis=1)
  ## equivalent to:
  for i in range(array.shape[axis]):
    if (array[:,i] == col.any():
      return i

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

import numpy as np
from itertools import chain, izip


def find(a, predicate, chunk_size=1024):
    """
    Find the indices of array elements that match the predicate.

    Parameters
    ----------
    a : array_like
        Input data, must be 1D.

    predicate : function
        A function which operates on sections of the given array, returning
        element-wise True or False for each data value.

    chunk_size : integer
        The length of the chunks to use when searching for matching indices.
        For high probability predicates, a smaller number will make this
        function quicker, similarly choose a larger number for low
        probabilities.

    Returns
    -------
    index_generator : generator
        A generator of (indices, data value) tuples which make the predicate
        True.

    See Also
    --------
    where, nonzero

    Notes
    -----
    This function is best used for finding the first, or first few, data values
    which match the predicate.

    Examples
    --------
    >>> a = np.sin(np.linspace(0, np.pi, 200))
    >>> result = find(a, lambda arr: arr > 0.9)
    >>> next(result)
    ((71, ), 0.900479032457)
    >>> np.where(a > 0.9)[0][0]
    71


    """
    if a.ndim != 1:
        raise ValueError('The array must be 1D, not {}.'.format(a.ndim))

    i0 = 0
    chunk_inds = chain(xrange(chunk_size, a.size, chunk_size), 
                 [None])

    for i1 in chunk_inds:
        chunk = a[i0:i1]
        for inds in izip(*predicate(chunk).nonzero()):
            yield (inds[0] + i0, ), chunk[inds]
        i0 = i1
In [1]: from np_utils import find

In [2]: import numpy as np

In [3]: import numpy.random    

In [4]: np.random.seed(1)

In [5]: a = np.random.randn(1e8)

In [6]: a.min(), a.max()
Out[6]: (-6.1194900990552776, 5.9632246301166321)

In [7]: next(find(a, lambda a: np.abs(a) > 6))
Out[7]: ((33105441,), -6.1194900990552776)

In [8]: (np.abs(a) > 6).nonzero()
Out[8]: (array([33105441]),)

In [9]: %timeit (np.abs(a) > 6).nonzero()
1 loops, best of 3: 1.51 s per loop

In [10]: %timeit next(find(a, lambda a: np.abs(a) > 6))
1 loops, best of 3: 912 ms per loop

In [11]: %timeit next(find(a, lambda a: np.abs(a) > 6, chunk_size=100000))
1 loops, best of 3: 470 ms per loop

In [12]: %timeit next(find(a, lambda a: np.abs(a) > 6, chunk_size=1000000))
1 loops, best of 3: 483 ms per loop

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

هتافات،

أعلم أن هذا قد تأخر 3 سنوات ، ولكن هل هذا مشمول في numpy الآن؟ قادمة من خلفية Matlab تبدو هذه الوظائف مهمة حقًا بالنسبة لي. العلاقات العامة ستكون محل تقدير كبير (ليس هذا أنا أحد المطورين).

سأكون مهتمًا أيضًا بهذا.

ربما يكون هذا واضحًا ، ولكن نظرًا لأنه لم يتم ذكره: np.all() و np.any() ربما يكون أسهل (ولا لبس فيه بالنسبة للبعد> 1) لجعل الكسول. حاليا...

In [2]: zz = np.zeros(shape=10000000)

In [3]: zz[0] = 1

In [4]: %timeit -r 1 -n 1 any(zz)
1 loop, best of 1: 3.52 µs per loop

In [5]: %timeit -r 1 -n 1 np.any(zz)
1 loop, best of 1: 16.7 ms per loop

(آسف ، فاتني المرجع إلى # 3446)

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

تجد المصدر والتفاصيل هنا:

https://pypi.python.org/pypi؟name=py_find_1st& : الإجراء = العرض

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

لقد وجدت هذا من منشور stackexchange يبحث عن هذه الميزة ، والتي تمت مشاهدتها أكثر من 70 ألف مرة. roebel هل تلقيت أي تعليقات على هذا؟ هل يمكنك فقط وضع إعلان عام للميزة ، والتي قد تحظى بمزيد من الاهتمام؟

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

https://anaconda.org/roebel/py_find_1st

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

هل إزالة " الأولوية: عادية " تعني أن هذه الميزة المهمة ستحظى بطريقة ما باهتمام أقل؟

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

ربما يكون مفيدًا هنا للإشارة إلى # 8528 ، والذي يمثل اسميًا حوالي all_equal ولكن يمكن اعتباره أجزاء تنفيذية لهذا. في الواقع ، في https://github.com/numpy/numpy/pull/8528#issuecomment -365358119 ، يقترح ahaldane صراحة تنفيذ طريقة تخفيض first على جميع عوامل المقارنة بدلاً من gufunc all_equal .

هذا يعني أيضًا أن هناك قدرًا كبيرًا من التنفيذ ينتظر أن يتم تكييفه (على الرغم من أنه ليس تغييرًا بسيطًا من gufunc إلى طريقة اختزال جديدة ، وهناك سؤال عما إذا كنا نريد طريقة جديدة على جميع ufuncs ، حتى تلك الخاصة بـ التي first لا معنى لها.

هذه المشكلة معروفة منذ (على الأقل) عام 2012. أي تحديث على طريقة لمنع nonzero(A)[0][0] من البحث عن A ؟

هل هي ما يسمى بالطريقة البيثونية للبحث دائمًا عن جميع العناصر؟

yunyoulu : إنها طريقة ufunc. دعنا نعود خطوة إلى الوراء ونلقي نظرة على العملية العامة للحساب متعدد الخطوات في numpy ، وعدد المرات التي يستغرقها:

  1. np.argwhere(x)[0] - ينفذ تمريرًا واحدًا للبيانات
  2. np.argwhere(f(x))[0] - ينفذ تمريرين من البيانات
  3. np.argwhere(f(g(x)))[0] - ينفذ 3 تمريرات للبيانات

قد يكون أحد الخيارات هو تقديم وظيفة np.first أو ما شابه - والتي ستبدو بعد ذلك كما يلي ، حيث يختلف k <= 1 اعتمادًا على مكان العنصر الأول:

  1. np.first(x)[0] - ينفذ تمرير 0 + k للبيانات
  2. np.first(f(x))[0] - يقوم بتمرير 1 + k للبيانات
  3. np.first(f(g(x)))[0] - ينفذ تمريرات 2 + k للبيانات

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


عفا عليها الزمن

@ eric-wieser

لا أعتقد أن صياغته صحيحة تمامًا. إذا كان k = 10 لبعض المشاكل ، فلن يكون 1+10=11 يمر من البيانات لـ np.first(f(x))[0]

(تم التعديل بواسطة @ eric-wieser للإيجاز ، هذه المحادثة طويلة جدًا بالفعل)

حالة الاستخدام التي أرى فيها الحاجة إلى هذه الوظيفة هي عندما يكون A موترًا كبيرًا بـ A.shape = (n_1, n_2, ..., n_m) . في مثل هذه الحالة ، سيتطلب np.first(A) النظر إلى عناصر k فقط من A بدلاً من n_1*n_2*...*n_m (توفير كبير محتمل).

أرى أن الحاجة إلى هذه الوظيفة هي عندما يكون A موترًا كبيرًا

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

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

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

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

np.first(f(x))[0] - يقوم بتمرير 1 + k للبيانات

@ eric-wieser بالفعل عندما نظرت في هذه المشكلة في عام 2017 كنت آمل حقًا أن تكون الخطوة الأولى نحو نوع من np.firstwhere(x, array_or_value_to_compare) ، وهي بالفعل حالة محددة - لكنها مهمة في تجربتي - f(x) .

toobaz : أفترض أن لديك f = lambda x: x == value_to_compare في هذا المثال.

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

  1. np.first(x) - حفظ بطاقة مقابل غير صفرية
  2. np.first_equal(x, v) - حفظ بطاقة مقابل first(np.equal(x, v))
  3. np.first_square_equal(x*x, v) - حفظ بطاقة مقابل first_equal(np.square(x), v)

يجب أن يكون واضحًا جدًا أن هذا لا مقياس على الإطلاق ، وعلينا رسم الخط في مكان ما. أنا أؤيد قليلاً السماح لـ 1 ، لكن السماح لـ 2 هو بالفعل انفجار في مساحة سطح API ، و 3 يبدو غير حكيم للغاية بالنسبة لي.

حجة واحدة لصالح np.first - إذا قمنا بتطبيقها ، فإن numba يمكن أن تكون حالة خاصة مثل أن np.first(x*x == v) _ ضمن سياق numba_ في الواقع _ يفعل_ يقوم بتمرير واحد.

على أي حال ، من الجيد معرفة أنه من المستحيل القيام بالأشياء البطيئة في numpy ، مما يوضح الوضع الحالي للمشكلة.

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

دعنا نطرح أسئلة بسيطة: هل تتوسع أجهزة الكمبيوتر الشخصية اليوم؟ الجواب بالتأكيد لا . قبل ثلاث سنوات عندما اشتريت جهاز كمبيوتر محمول عاديًا ، كان الجهاز مزودًا بذاكرة 8 جيجابايت ؛ والآن ستجد 8 غيغابايت في السوق. ومع ذلك ، يستخدم كل برنامج ذاكرة 2x أو 4x أكثر مما اعتادوا عليه. على الأقل لا يتم تغيير حجم محطات العمل بالطريقة نفسها التي تعمل بها المجموعات.

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

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

toobaz : أفترض أن لديك f = lambda x: x == value_to_compare في هذا المثال.

صيح

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

1. `np.first(x)` - save a pass vs nonzero

2. `np.first_equal(x, v)` - save a pass vs `first(np.equal(x, v))`

3. `np.first_square_equal(x*x, v)` - save a pass vs `first_equal(np.square(x), v)`

أتفهم قلقك ، لكنني لن أطلب أبدًا np.first_square_equal تمامًا مثلما لم أطلب أبدًا (وآمل أن يسأل أحد) عن np.square_where . ونعم ، أرى أن ذلك يعني إجراء تمرير كامل للبيانات إذا قمت بذلك. 3. ولكن تم إنشاء v مرة واحدة ، وقد أحتاج إلى البحث عن العديد من القيم المختلفة لـ x فوقها . على سبيل المثال (أعود للبساطة إلى المثال 2.) ، أريد التحقق مما إذا كانت جميع الفئات الثلاثين الممكنة تظهر في مصفوفة عناصر 10 ^ 9 - وأظن بشدة أنها تظهر جميعًا ضمن أول 10 ^ 3 عناصر.

لذلك اسمحوا لي أولاً بتوضيح تعليقي السابق: أود np.firstwhere(x, array_or_value_to_compare) كوظيفة تتوافق مع حدسي ، لكن المشكلات الحسابية التي كنت أعود إليها في عام 2017 كان من الممكن حلها حتى مع np.first .

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

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

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

مرحبا بول،

لقد أجريت معيارًا صغيرًا لمقارنة الحل الخاص بك بـ np.flatnonzero وامتداد py_find_1st الخاص بي.

تجد المعيار مرفقًا.

هنا النتائج

(قاعدة) m3088.roebel: (اختبار) (g: master) 514> ./benchmark.py
utf1st.find_1st (rr، limit، utf1st.cmp_equal) ::
وقت التشغيل 0.131 ثانية
np.flatnonzero (rr == الحد) [0] ::
وقت التشغيل 2.121 ثانية
التالي ((ii for ii ، vv في تعداد (rr) إذا كان vv == limit)) ::
وقت التشغيل 1.612 ثانية

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

أفضل
أكسل

تعديل:
يبدو أن الرسالة التي قمت بالرد عليها عن طريق البريد قد اختفت ، وكذلك المعيار المرتبط ببريدي. المعيار هنا

https://github.com/roebel/py_find_1st/blob/master/test/benchmark.py

آسف على الضوضاء.

في 15/05/2020 17:33 ، كتب ب.

ماذا عن | next (i for i، v في تعداد (x) إذا كان v) |؟

-
أنت تتلقى هذا لأنه تم ذكرك.
قم بالرد على هذه الرسالة الإلكترونية مباشرةً ، أو اعرضها على GitHub https://github.com/numpy/numpy/issues/2269#issuecomment-629314457 ، أو إلغاء الاشتراك
https://github.com/notifications/unsubscribe-auth/ACAL2LS2YZALARHBHNABVILRRVOEPANCNFSM4ABV5HGA .

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