यहाँ एक ओ (अधिकतम (x) + लेन (x)) का उपयोग कर दृष्टिकोण है scipy.sparse
:
import numpy as np
from scipy import sparse
x = np.array("1 2 2 0 0 1 3 5".split(),int)
x
# array([1, 2, 2, 0, 0, 1, 3, 5])
M,N = x.max()+1,x.size
sparse.csc_matrix((x,x,np.arange(N+1)),(M,N)).tolil().rows.tolist()
# [[3, 4], [0, 5], [1, 2], [6], [], [7]]
यह पदों पर प्रविष्टियों के साथ विरल मैट्रिक्स बनाकर काम करता है (x [0], 0), (x [1], 1), ... CSC
(संपीड़ित विरल स्तंभ) प्रारूप का उपयोग करना यह सरल है। तब मैट्रिक्स को LIL
(लिंक की गई सूची) प्रारूप में बदल दिया जाता है । यह प्रारूप प्रत्येक पंक्ति के कॉलम कॉलम को अपनी rows
विशेषता में एक सूची के रूप में संग्रहीत करता है , इसलिए हमें केवल इतना करना है कि इसे ले जाएं और इसे सूची में परिवर्तित करें।
ध्यान दें कि छोटे सरणियों के लिए argsort
आधारित समाधान संभवतः तेज़ होते हैं लेकिन कुछ बड़े आकार के नहीं होते हैं।
संपादित करें:
argsort
-बेड- numpy
ऑन सॉल्यूशन:
np.split(x.argsort(kind="stable"),np.bincount(x)[:-1].cumsum())
# [array([3, 4]), array([0, 5]), array([1, 2]), array([6]), array([], dtype=int64), array([7])]
यदि समूहों के भीतर सूचकांकों का क्रम मायने नहीं रखता है, तो आप भी कोशिश कर सकते हैं argpartition
(यह इस छोटे उदाहरण में कोई फर्क नहीं पड़ता है लेकिन सामान्य रूप से इसकी गारंटी नहीं है):
bb = np.bincount(x)[:-1].cumsum()
np.split(x.argpartition(bb),bb)
# [array([3, 4]), array([0, 5]), array([1, 2]), array([6]), array([], dtype=int64), array([7])]
संपादित करें:
@Divakar के इस्तेमाल के खिलाफ सिफारिश की गई है np.split
। इसके बजाय, एक लूप शायद तेज है:
A = x.argsort(kind="stable")
B = np.bincount(x+1).cumsum()
[A[B[i-1]:B[i]] for i in range(1,len(B))]
या आप बिल्कुल नए (Python3.8 +) वालरस ऑपरेटर का उपयोग कर सकते हैं:
A = x.argsort(kind="stable")
B = np.bincount(x)
L = 0
[A[L:(L:=L+b)] for b in B.tolist()]
संपादित करें (संपादित):
(शुद्ध सुन्न नहीं): सुंबा के विकल्प के रूप में (@ प्रेषक का पद देखें) हम पाइथ्रान का भी उपयोग कर सकते हैं।
संकलन pythran -O3 <filename.py>
import numpy as np
#pythran export sort_to_bins(int[:],int)
def sort_to_bins(idx, mx):
if mx==-1:
mx = idx.max() + 1
cnts = np.zeros(mx + 2, int)
for i in range(idx.size):
cnts[idx[i] + 2] += 1
for i in range(3, cnts.size):
cnts[i] += cnts[i-1]
res = np.empty_like(idx)
for i in range(idx.size):
res[cnts[idx[i]+1]] = i
cnts[idx[i]+1] += 1
return [res[cnts[i]:cnts[i+1]] for i in range(mx)]
यहाँ numba
एक प्रदर्शन-वार द्वारा जीता जाता है:
repeat(lambda:enum_bins_numba_buffer(x),number=10)
# [0.6235917090671137, 0.6071486569708213, 0.6096088469494134]
repeat(lambda:sort_to_bins(x,-1),number=10)
# [0.6235359431011602, 0.6264424560358748, 0.6217901279451326]
पुराना सामान:
import numpy as np
#pythran export bincollect(int[:])
def bincollect(a):
o = [[] for _ in range(a.max()+1)]
for i,j in enumerate(a):
o[j].append(i)
return o
समय बनाम सुब्बा (पुराना)
timeit(lambda:bincollect(x),number=10)
# 3.5732191529823467
timeit(lambda:enumerate_bins(x),number=10)
# 6.7462647299980745
np.argsort([1, 2, 2, 0, 0, 1, 3, 5])
देता हैarray([3, 4, 0, 5, 1, 2, 6, 7], dtype=int64)
। तो आप बस अगले तत्वों की तुलना कर सकते हैं।