एक सरणी में रैंक आइटम पायथन / NumPy का उपयोग करते हुए, दो बार सरणी को छांटे बिना


100

मेरे पास संख्याओं की एक सरणी है और मैं एक और सरणी बनाना चाहूंगा जो पहले सरणी में प्रत्येक आइटम के रैंक का प्रतिनिधित्व करता है। मैं पायथन और न्यूमपी का उपयोग कर रहा हूं।

उदाहरण के लिए:

array = [4,2,7,1]
ranks = [2,1,3,0]

यहाँ सबसे अच्छी विधि है जिसके साथ मैं आया हूँ:

array = numpy.array([4,2,7,1])
temp = array.argsort()
ranks = numpy.arange(len(array))[temp.argsort()]

क्या कोई बेहतर / तेज़ विधियाँ हैं जो दो बार सरणी को छाँटने से बचती हैं?


6
आपकी अंतिम पंक्ति के बराबर है ranks = temp.argsort()
स्वेन मार्नाच

जवाबों:


67

अंतिम चरण में बायीं ओर स्लाइसिंग का उपयोग करें:

array = numpy.array([4,2,7,1])
temp = array.argsort()
ranks = numpy.empty_like(temp)
ranks[temp] = numpy.arange(len(array))

यह अंतिम चरण में क्रमपरिवर्तन द्वारा दो बार छँटाई से बचा जाता है।


3
पूर्ण धन्यवाद! मुझे पता था कि एक समाधान है और यह स्पष्ट होगा कि मैंने इसे देखा था। मैंने समयसीमा के साथ कुछ परीक्षण किया, और यह तरीका छोटे सरणियों के लिए थोड़ा धीमा है। मेरी मशीन पर वे बराबर हैं जब सरणी में 2,000 तत्व हैं। 20,000 तत्वों पर, आपकी विधि लगभग 25% तेज है।
जोशसर्स

यह कैसे करना है पर कोई सिफारिश?
एक्ससेर

1 से अधिक मंद के लिए नीचे उत्तर देखें।
गणित

100

सरणी के क्रम को प्राप्त करने के लिए, फिर रैंकिंग प्राप्त करने के लिए, पहले दो बार आर्गन्सोर्ट का उपयोग करें:

array = numpy.array([4,2,7,1])
order = array.argsort()
ranks = order.argsort()

2 डी (या उच्च आयामी) सरणियों के साथ काम करते समय, सही अक्ष पर ऑर्डर करने के लिए एक तर्क तर्क को पास करने के लिए सुनिश्चित करें।


2
ध्यान दें कि यदि संख्या आपके इनपुट सरणी में दोहराई जाती है (जैसे। [4,2,7,1,1]) आउटपुट उन संख्याओं को उनके सरणी स्थिति ( [3,2,4,0,1]) के आधार पर रैंक करेगा
rcoup

4
दो बार छंटाई अक्षम है। @Sven Marnach का उत्तर दिखाता है कि रैंकिंग को एक कॉल के साथ कैसे पूरा किया जाए argsort
वॉरेन वीकेसर

6
@WarrenWeckesser: मैंने अभी दोनों के बीच अंतर का परीक्षण किया है, और आप बड़े सरणियों के लिए सही हैं, लेकिन कुछ भी छोटे (n <100) के लिए, डबल argsort तेज है (लगभग 20% n = 100 के लिए, और लगभग 5 गुना अधिक तेज़ है n = 10 के लिए)। इसलिए यदि आपको बहुत सारे मानों के बहुत सारे सेट पर बहुत सारी रैंकिंग करनी है, तो यह तरीका बहुत बेहतर है।
n

3
@ArrenWeckesser: वास्तव में, मैं गलत हूं, यह तरीका बेहतर है। दोनों विधियां scipy.stats विधि की तुलना में बहुत तेज हैं, भी। परिणाम: gist.github.com/naught101/14042d91a2d0f18a6ae4
0101

1
@ naught101: आपकी स्क्रिप्ट में एक बग है। लाइन array = np.random.rand(10)होनी चाहिए array = np.random.rand(n)
वॉरेन वीकेसर

88

यह प्रश्न कुछ साल पुराना है, और स्वीकृत उत्तर बहुत अच्छा है, लेकिन मुझे लगता है कि निम्नलिखित अभी भी ध्यान देने योग्य है। यदि आप पर निर्भरता को बुरा नहीं मानते हैं scipy, तो आप इसका उपयोग कर सकते हैं scipy.stats.rankdata:

In [22]: from scipy.stats import rankdata

In [23]: a = [4, 2, 7, 1]

In [24]: rankdata(a)
Out[24]: array([ 3.,  2.,  4.,  1.])

In [25]: (rankdata(a) - 1).astype(int)
Out[25]: array([2, 1, 3, 0])

इसकी एक अच्छी विशेषता rankdataयह है कि methodतर्क संबंधों को संभालने के लिए कई विकल्प प्रदान करता है। उदाहरण के लिए, 20 की तीन घटनाएं और 40 की दो घटनाएं हैं b:

In [26]: b = [40, 20, 70, 10, 20, 50, 30, 40, 20]

डिफ़ॉल्ट बंधे मूल्यों के लिए औसत रैंक प्रदान करता है:

In [27]: rankdata(b)
Out[27]: array([ 6.5,  3. ,  9. ,  1. ,  3. ,  8. ,  5. ,  6.5,  3. ])

method='ordinal' लगातार रैंक प्रदान करता है:

In [28]: rankdata(b, method='ordinal')
Out[28]: array([6, 2, 9, 1, 3, 8, 5, 7, 4])

method='min' सभी बंधे मूल्यों के लिए बंधे हुए मूल्यों की न्यूनतम रैंक प्रदान करता है:

In [29]: rankdata(b, method='min')
Out[29]: array([6, 2, 9, 1, 2, 8, 5, 6, 2])

अधिक विकल्पों के लिए docstring देखें।


1
हाँ, यह कहीं भी सबसे अच्छा जवाब है जहाँ किनारे के मामले महत्वपूर्ण हैं।
n

मुझे यह दिलचस्प rankdataलगता है कि आंतरिक रूप से प्रारंभिक रैंकिंग उत्पन्न करने के लिए स्वीकृत उत्तर के रूप में उसी तंत्र का उपयोग करना प्रतीत होता है।
एलेक्सवी

5

मैंने सरणियों के लिए दोनों समाधान को एक से अधिक आयामों तक विस्तारित करने की कोशिश की, जिससे आप अपने सरणी पंक्ति-दर-पंक्ति (अक्ष = 1) को संसाधित कर सकते हैं।

मैंने पंक्तियों पर एक लूप के साथ पहला कोड बढ़ाया; शायद इसमें सुधार किया जा सकता है

temp = A.argsort(axis=1)
rank = np.empty_like(temp)
rangeA = np.arange(temp.shape[1])
for iRow in xrange(temp.shape[0]): 
    rank[iRow, temp[iRow,:]] = rangeA

और दूसरा, k.rooijers सुझाव के बाद, बन जाता है:

temp = A.argsort(axis=1)
rank = temp.argsort(axis=1)

मैंने आकार (1000,100) के साथ बेतरतीब ढंग से 400 सरणियों का निर्माण किया; पहले कोड में लगभग 7.5 था, दूसरा 3.8 था।


5

एक औसत रैंक के वेक्टर संस्करण के लिए, नीचे देखें। मुझे np.unique से प्यार है, यह वास्तव में उस दायरे को चौड़ा करता है जो कोड और कुशलता से वेक्टर नहीं किया जा सकता है। इसके अलावा अजगर के लिए छोरों से बचने से, यह दृष्टिकोण भी 'ए' पर निहित डबल लूप से बचा जाता है।

import numpy as np

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

a = np.array([4,2,7,2,1])
rank = a.argsort().argsort()

unique, inverse = np.unique(a, return_inverse = True)

unique_rank_sum = np.zeros_like(unique)
np.add.at(unique_rank_sum, inverse, rank)
unique_count = np.zeros_like(unique)
np.add.at(unique_count, inverse, 1)

unique_rank_mean = unique_rank_sum.astype(np.float) / unique_count

rank_mean = unique_rank_mean[inverse]

print rank_mean

वैसे; मैंने इस कोड को अन्य औसत रैंक कोड के समान आउटपुट का उत्पादन करने के लिए बनाया था, लेकिन मैं संख्याओं को दोहराने के समूह के न्यूनतम रैंक की कल्पना कर सकता हूं। इसे और भी आसानी से प्राप्त किया जा सकता है >>> अद्वितीय, सूचकांक, व्युत्क्रम = np.unique (ए, ट्रू, ट्रू) >>> रैंक_मिन = रैंक [इंडेक्स] [उलटा]
ईलको हूगेडोर्नो

AttributeError:: मैं अपने समाधान (numpy 1.7.1) के साथ निम्न त्रुटि हो रही है 'numpy.ufunc' वस्तु 'पर' कोई विशेषता है
डर

इसके लिए हाल ही के अधिक संस्करण की आवश्यकता है; तुम्हारा काफी प्राचीन है
इल्को हुगेंडोर्न

4

लालित्य और समाधान की कमी के अलावा, प्रदर्शन का सवाल भी है। यहाँ थोड़ा बेंचमार्क है:

import numpy as np
from scipy.stats import rankdata
l = list(reversed(range(1000)))

%%timeit -n10000 -r5
x = (rankdata(l) - 1).astype(int)
>>> 128 µs ± 2.72 µs per loop (mean ± std. dev. of 5 runs, 10000 loops each)

%%timeit -n10000 -r5
a = np.array(l)
r = a.argsort().argsort()
>>> 69.1 µs ± 464 ns per loop (mean ± std. dev. of 5 runs, 10000 loops each)

%%timeit -n10000 -r5
a = np.array(l)
temp = a.argsort()
r = np.empty_like(temp)
r[temp] = np.arange(len(a))
>>> 63.7 µs ± 1.27 µs per loop (mean ± std. dev. of 5 runs, 10000 loops each)

1
अच्छा विचार है, लेकिन एक निष्पक्ष तुलना के लिए, आपको उपयोग करना चाहिए rankdata(l, method='ordinal') - 1
वॉरेन वीकेसर


2

मैंने उपरोक्त तरीकों की कोशिश की, लेकिन असफल रहा क्योंकि मेरे पास कई झरोखे थे। हां, यहां तक ​​कि फ्लोट्स के साथ डुप्लिकेट आइटम महत्वपूर्ण हो सकते हैं।

इसलिए मैंने टाई-चेकिंग चरण जोड़कर एक संशोधित 1D समाधान लिखा:

def ranks (v):
    import numpy as np
    t = np.argsort(v)
    r = np.empty(len(v),int)
    r[t] = np.arange(len(v))
    for i in xrange(1, len(r)):
        if v[t[i]] <= v[t[i-1]]: r[t[i]] = r[t[i-1]]
    return r

# test it
print sorted(zip(ranks(v), v))

मेरा मानना ​​है कि यह जितना कुशल हो सकता है।


0

मुझे k.rooijers द्वारा विधि पसंद आई, लेकिन जैसा कि rcoup ने लिखा है, दोहराया संख्या को सरणी स्थिति के अनुसार क्रमबद्ध किया गया है। यह मेरे लिए अच्छा नहीं था, इसलिए मैंने रैंक को पोस्टप्रोसेस करने और किसी भी दोहराया संख्या को एक संयुक्त औसत रैंक में विलय करने के लिए संस्करण को संशोधित किया:

import numpy as np
a = np.array([4,2,7,2,1])
r = np.array(a.argsort().argsort(), dtype=float)
f = a==a
for i in xrange(len(a)):
   if not f[i]: continue
   s = a == a[i]
   ls = np.sum(s)
   if ls > 1:
      tr = np.sum(r[s])
      r[s] = float(tr)/ls
   f[s] = False

print r  # array([ 3. ,  1.5,  4. ,  1.5,  0. ])

मुझे आशा है कि यह दूसरों की भी मदद कर सकता है, मैंने इस के लिए माता के समाधान खोजने की कोशिश की, लेकिन कोई भी नहीं मिल सका ...


0

आर्ग्सोर्ट और स्लाइस समरूपता संचालन हैं।

दो बार argsort के बजाय दो बार स्लाइस का प्रयास करें। चूंकि स्लाइस आर्गोसॉर्ट से तेज है

array = numpy.array([4,2,7,1])
order = array.argsort()
ranks = np.arange(array.shape[0])[order][order]

0

उत्तरों में से एक का अधिक सामान्य संस्करण:

In [140]: x = np.random.randn(10, 3)

In [141]: i = np.argsort(x, axis=0)

In [142]: ranks = np.empty_like(i)

In [143]: np.put_along_axis(ranks, i, np.repeat(np.arange(x.shape[0])[:,None], x.shape[1], axis=1), axis=0)

2 से अधिक आयामों में सूचकांक के रूप में numpy.argsort () का उपयोग कैसे करें देखें ? अधिक dims को सामान्य करने के लिए।

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