นี่คือวิธี O (สูงสุด (x) + len (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
- based 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))]
หรือคุณสามารถใช้ตัวดำเนินการ walrus (Python3.8 +) ใหม่เอี่ยม:
A = x.argsort(kind="stable")
B = np.bincount(x)
L = 0
[A[L:(L:=L+b)] for b in B.tolist()]
แก้ไข (แก้ไข):
(ไม่บริสุทธิ์ numpy): เป็นทางเลือกแทน numba (ดูโพสต์ @ ของ senderle) เรายังสามารถใช้ pythran
รวบรวมกับ 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
การกำหนดเวลาเทียบกับ numba (เก่า)
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)
จะช่วยให้ จากนั้นคุณสามารถเปรียบเทียบองค์ประกอบต่อไป