_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
شائعًا جدًا.
_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 ، وعدد المرات التي يستغرقها:
np.argwhere(x)[0]
- ينفذ تمريرًا واحدًا للبياناتnp.argwhere(f(x))[0]
- ينفذ تمريرين من البياناتnp.argwhere(f(g(x)))[0]
- ينفذ 3 تمريرات للبياناتقد يكون أحد الخيارات هو تقديم وظيفة np.first
أو ما شابه - والتي ستبدو بعد ذلك كما يلي ، حيث يختلف k <= 1
اعتمادًا على مكان العنصر الأول:
np.first(x)[0]
- ينفذ تمرير 0 + k للبياناتnp.first(f(x))[0]
- يقوم بتمرير 1 + k للبيانات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). إذا لم تكن حريصًا ، فسننتهي بـ (التهجئات التخمينية):
np.first(x)
- حفظ بطاقة مقابل غير صفريةnp.first_equal(x, v)
- حفظ بطاقة مقابل first(np.equal(x, v))
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 .
التعليق الأكثر فائدة
أعلم أن هذا قد تأخر 3 سنوات ، ولكن هل هذا مشمول في numpy الآن؟ قادمة من خلفية Matlab تبدو هذه الوظائف مهمة حقًا بالنسبة لي. العلاقات العامة ستكون محل تقدير كبير (ليس هذا أنا أحد المطورين).