क्या अवरोही क्रम में argsort का उपयोग करना संभव है?


181

निम्नलिखित कोड पर विचार करें:

avgDists = np.array([1, 8, 6, 9, 4])
ids = avgDists.argsort()[:n]

यह मुझे nसबसे छोटे तत्वों के संकेत देता है । क्या उच्चतम तत्वों argsortके सूचकांक प्राप्त करने के लिए अवरोही क्रम में इसका उपयोग करना संभव है n?


3
क्या यह सरल नहीं है ids = np.array(avgDists).argsort()[-n:]?
जैमे

2
@ जय: नहीं, यह काम नहीं करता है। 'सही जवाब' है [3, 1, 2]। आपकी लाइन का उत्पादन होता है [2, 1, 3](यदि उदाहरण के रूप में n == 3)
dawg

2
@ ड्रूक वेल, फिर इसे बनाओ ids = np.array(avgDists).argsort()[-n:][::-1]। बात पूरी सूची की एक प्रति बनाने से बच रही है, जो कि आपके -सामने आने पर आपको मिलती है। ओपी के छोटे उदाहरण के लिए प्रासंगिक नहीं, बड़े मामलों के लिए हो सकता है।
जयम

1
@ जयम: आप सही कह रहे हैं। देखिये मेरा अपडेटेड जवाब। सिंटैक्स थियो समाप्ति स्लाइस पर आपकी टिप्पणी से ठीक विपरीत है: np.array(avgDists).argsort()[::-1][:n]यह करेंगे। इसके अलावा, यदि आप numpy का उपयोग करने जा रहे हैं, तो numpy रहें। पहले सूची को एक सरणी में परिवर्तित करें: avgDist=np.array(avgDists)फिर यह बन जाता हैavgDist.argsort()[::-1][:n}
dawg

जवाबों:


230

यदि आप किसी सरणी को नकारते हैं, तो निम्नतम तत्व उच्चतम तत्व बन जाते हैं और इसके विपरीत। इसलिए, nउच्चतम तत्वों के सूचक हैं:

(-avgDists).argsort()[:n]

इसके बारे में एक और तरीका, जैसा कि टिप्पणियों में बताया गया है , निरीक्षण करना है कि बड़े तत्व आर्गन्स में अंतिम आ रहे हैं । तो, आप nउच्चतम तत्वों को खोजने के लिए आर्गन्स की पूंछ से पढ़ सकते हैं :

avgDists.argsort()[::-1][:n]

दोनों विधियाँ समय जटिलता में ओ (एन लॉग एन) हैं , क्योंकि argsortकॉल यहां प्रमुख शब्द है। लेकिन दूसरे दृष्टिकोण का एक अच्छा लाभ है: यह O (1) स्लाइस के साथ सरणी के O (n) नकार को प्रतिस्थापित करता है । यदि आप छोरों के अंदर छोटे सरणियों के साथ काम कर रहे हैं, तो आपको उस नकारात्मकता से बचने से कुछ प्रदर्शन लाभ मिल सकते हैं, और यदि आप विशाल सरणियों के साथ काम कर रहे हैं, तो आप मेमोरी उपयोग पर बचत कर सकते हैं क्योंकि नकार पूरे सरणी की एक प्रति बनाता है।

ध्यान दें कि ये विधियाँ हमेशा समान परिणाम नहीं देती हैं: यदि स्थिर argsortतर्क को लागू करने का अनुरोध किया जाता है , जैसे कि कीवर्ड तर्क को पारित करके kind='mergesort', तो पहली रणनीति छँटाई स्थिरता को बनाए रखेगी, लेकिन दूसरी रणनीति स्थिरता को तोड़ देगी (अर्थात समान पदों को आइटम उलट हो जाएंगे)।

उदाहरण समय:

100 फ़्लोट्स की एक छोटी सरणी और 30 पूंछ की लंबाई का उपयोग करते हुए, दृश्य विधि लगभग 15% तेज थी

>>> avgDists = np.random.rand(100)
>>> n = 30
>>> timeit (-avgDists).argsort()[:n]
1.93 µs ± 6.68 ns per loop (mean ± std. dev. of 7 runs, 1000000 loops each)
>>> timeit avgDists.argsort()[::-1][:n]
1.64 µs ± 3.39 ns per loop (mean ± std. dev. of 7 runs, 1000000 loops each)
>>> timeit avgDists.argsort()[-n:][::-1]
1.64 µs ± 3.66 ns per loop (mean ± std. dev. of 7 runs, 1000000 loops each)

बड़े सरणियों के लिए, argort प्रमुख है और कोई महत्वपूर्ण समय अंतर नहीं है

>>> avgDists = np.random.rand(1000)
>>> n = 300
>>> timeit (-avgDists).argsort()[:n]
21.9 µs ± 51.2 ns per loop (mean ± std. dev. of 7 runs, 10000 loops each)
>>> timeit avgDists.argsort()[::-1][:n]
21.7 µs ± 33.3 ns per loop (mean ± std. dev. of 7 runs, 10000 loops each)
>>> timeit avgDists.argsort()[-n:][::-1]
21.9 µs ± 37.1 ns per loop (mean ± std. dev. of 7 runs, 10000 loops each)

कृपया ध्यान दें कि नीचे nedim की टिप्पणी गलत है। उलटने से पहले या बाद में छंटनी करना दक्षता में कोई अंतर नहीं रखता है, क्योंकि ये दोनों ऑपरेशन केवल सरणी के दृश्य को अलग-अलग तरीके से दिखा रहे हैं और वास्तव में डेटा की प्रतिलिपि नहीं बना रहे हैं।


14
यह उलटने से पहले टुकड़ा करने के लिए और भी अधिक कुशल है, अर्थातnp.array(avgDists).argsort()[:-n][::-1]
nedim

3
यदि मूल सरणी में nans हैं तो ये उत्तर समतुल्य नहीं हैं। ऐसे मामले में, पहला समाधान शुरुआत के बजाय अंत में नैन्स के साथ अधिक प्राकृतिक परिणाम देना प्रतीत होता है।
फीलचेनफेल्ट

1
जब एक स्थिर प्रकार वांछित होता है तो ये तुलना कैसे करते हैं? संभवतः स्लाइसिंग स्ट्रेटेजी समान वस्तुओं को उलट देती है?
एरिक

1
@ user3666197 मुझे लगा कि यह उत्तर के लिए प्रासंगिक नहीं था। नकार की प्रतिलिपि बनाता है या नहीं (यह करता है) यहां वास्तव में महत्वपूर्ण नहीं है, प्रासंगिक जानकारी यह है कि नकारात्मक की गणना हे (एन) जटिलता बनाम एक और टुकड़ा लेना है जो ओ (1) है
विम

1
@ user3666197 हां, यह एक अच्छा बिंदु है - यदि कोई सरणी 50% उपलब्ध मेमोरी ले रही है, तो हम निश्चित रूप से इसे कॉपी करने और स्वैपिंग से बचना चाहेंगे। मैं यह बताने के लिए फिर से संपादन करूंगा कि एक प्रति वहां बनाई गई है।
विम

70

पाइथन की तरह ही, इसके [::-1]द्वारा लौटे हुए सरणी को उलट देता है argsort()और [:n]अंतिम n तत्वों को देता है:

>>> avgDists=np.array([1, 8, 6, 9, 4])
>>> n=3
>>> ids = avgDists.argsort()[::-1][:n]
>>> ids
array([3, 1, 2])

इस विधि का लाभ यह है कि avgDists idsका एक दृश्य है:

>>> ids.flags
  C_CONTIGUOUS : False
  F_CONTIGUOUS : False
  OWNDATA : False
  WRITEABLE : True
  ALIGNED : True
  UPDATEIFCOPY : False

('OWNDATA' गलत होने का संकेत है कि यह एक दृश्य है, कॉपी नहीं है)

ऐसा करने का एक और तरीका कुछ इस तरह है:

(-avgDists).argsort()[:n]

समस्या यह है कि जिस तरह से यह काम करता है वह सरणी में प्रत्येक तत्व का नकारात्मक बनाना है:

>>> (-avgDists)
array([-1, -8, -6, -9, -4])

एएनडी ऐसा करने के लिए एक प्रति बनाता है:

>>> (-avgDists_n).flags['OWNDATA']
True

यदि आप प्रत्येक समय, इस बहुत छोटे डेटा सेट के साथ हैं:

>>> import timeit
>>> timeit.timeit('(-avgDists).argsort()[:3]', setup="from __main__ import avgDists")
4.2879798610229045
>>> timeit.timeit('avgDists.argsort()[::-1][:3]', setup="from __main__ import avgDists")
2.8372560259886086

दृश्य विधि काफी तेज है (और 1/2 मेमोरी का उपयोग करता है ...)


4
यह उत्तर अच्छा है, लेकिन मुझे लगता है कि आपके शब्दों के गलत प्रदर्शन की वास्तविक विशेषताओं को गलत तरीके से प्रस्तुत किया गया है: "यहां तक ​​कि इस बहुत छोटे डेटा सेट के साथ, दृश्य विधि काफी तेज है" । वास्तव में, नकार O (n) है और argsort O (n log n) है । इसका मतलब है कि समय की विसंगति बड़े डेटा सेटों के लिए कम हो जाएगी - ओ (एन लॉग एन) शब्द हावी है, हालांकि आपका सुझाव ओ (एन) भाग का एक अनुकूलन है । इसलिए जटिलता समान है, और यह विशेष रूप से सेट किए गए इस छोटे डेटा के लिए है कि हम कोई महत्वपूर्ण अंतर देखते हैं।
विम

2
Asymptotically समकक्ष जटिलता का मतलब अभी भी हो सकता है कि एक एल्गोरिथ्म asymptotically दूसरे के रूप में दो बार तेजी से है। ऐसे भेदों को दूर फेंकने के परिणाम हो सकते हैं। उदाहरण के लिए, भले ही समय विसंगति (एक प्रतिशत के रूप में) दृष्टिकोण 0 करता है, मैं शर्त लगाने के लिए तैयार हूं कि नकार के साथ एल्गोरिथ्म अभी भी दो बार अधिक मेमोरी का उपयोग करता है।
बग

@bug यह कर सकता है, लेकिन यह इस मामले में नहीं है। मैंने अपने उत्तर में कुछ समय जोड़ा है। संख्याओं से पता चलता है कि बड़े सरणियों के लिए इन दृष्टिकोणों में समान समय है, जो कि परिकल्पना का समर्थन करता है जो कि argort प्रमुख है। नकार के लिए, मुझे लगता है कि आप मेमोरी के उपयोग के बारे में सही हैं, लेकिन उपयोगकर्ता अभी भी पसंद कर सकते हैं कि अगर वे नैनो की स्थिति के बारे में परवाह करते हैं और / या एक स्थिर प्रकार की आवश्यकता है।
विम

6

आप कमांड का उपयोग करके सॉर्ट करने के बाद फ्लिप कमांड का उपयोग कर सकते हैं numpy.flipud()या numpy.fliplr()अनुक्रमित को अवरोही क्रम में प्राप्त कर सकते हैं argsort। Thats मैं आमतौर पर क्या करते हैं।


यही कारण है कि टुकड़ा करने की क्रिया तुलना में बहुत धीमी है stackoverflow.com/a/44921013/125507
endolith

5

उपयोग करने के बजाय np.argsortआप उपयोग कर सकते हैं np.argpartition- यदि आपको केवल सबसे कम / उच्चतम n तत्वों के सूचकांकों की आवश्यकता है।

इसके लिए पूरे सरणी को क्रमबद्ध करने की आवश्यकता नहीं है, लेकिन आपको जिस भाग की आवश्यकता है, लेकिन ध्यान दें कि "आपके विभाजन के अंदर का क्रम" अपरिभाषित है, इसलिए जब यह सही संकेत देता है तो वे सही तरीके से क्रमबद्ध नहीं हो सकते हैं:

>>> avgDists = [1, 8, 6, 9, 4]
>>> np.array(avgDists).argpartition(2)[:2]  # indices of lowest 2 items
array([0, 4], dtype=int64)

>>> np.array(avgDists).argpartition(-2)[-2:]  # indices of highest 2 items
array([1, 3], dtype=int64)

या, यदि आप दो का एक साथ उपयोग कर रहे हैं, तो यह है कि argsort और argpartition, ऑपरेशन argpartition ऑपरेशन पर किया जाना है।
डिमांगोलेम

3

आप सरणी की एक प्रति बना सकते हैं और फिर प्रत्येक तत्व को -1 से गुणा कर सकते हैं।
एक प्रभाव के रूप में सबसे बड़ा तत्व सबसे छोटा हो जाएगा।
प्रतिलिपि में सबसे छोटे तत्वों के मूल मल मूल में सबसे बड़े तत्व हैं।


यह आसानी से सरणी को नकारने के रूप में किया जाता है, जैसा कि अन्य उत्तरों में कहा गया है:-array
onofricamila

1

अपने उदाहरण के साथ:

avgDists = np.array([1, 8, 6, 9, 4])

N अधिकतम मानों के अनुक्रमित प्राप्त करें:

ids = np.argpartition(avgDists, -n)[-n:]

उन्हें अवरोही क्रम में क्रमबद्ध करें:

ids = ids[np.argsort(avgDists[ids])[::-1]]

परिणाम प्राप्त करें (n = 4 के लिए):

>>> avgDists[ids]
array([9, 8, 6, 4])

1

जैसा कि @Kanmani ने संकेत दिया है, कार्यान्वयन की व्याख्या करने में आसान numpy.flipनिम्नलिखित के रूप में उपयोग कर सकते हैं :

import numpy as np

avgDists = np.array([1, 8, 6, 9, 4])
ids = np.flip(np.argsort(avgDists))
print(ids)

सदस्य कार्यों के बजाय आगंतुक पैटर्न का उपयोग करके, संचालन के आदेश को पढ़ना आसान है।


-1

एक और तरीका यह है कि: "df [np.argsort (-F [[0,]]]]] के रूप में तर्क के लिए तर्क में केवल '-' का उपयोग करें, बशर्ते df डेटाफ्रेम है और आप इसे पहले से सॉर्ट करना चाहते हैं कॉलम (कॉलम नंबर '0' द्वारा दर्शाया गया है)। कॉलम-नाम को उपयुक्त रूप में बदलें। बेशक, कॉलम को एक संख्यात्मक होना चाहिए।

हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.