Có thể sử dụng argsort theo thứ tự giảm dần?


181

Hãy xem xét các mã sau đây:

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

Điều này cho tôi chỉ số của các nyếu tố nhỏ nhất. Có thể sử dụng tương tự argsorttheo thứ tự giảm dần để có được các chỉ số của ncác yếu tố cao nhất?


3
Nó không đơn giản ids = np.array(avgDists).argsort()[-n:]sao?
Jaime

2
@Jaime: Không, điều đó không hiệu quả. "Câu trả lời đúng" là [3, 1, 2]. Dòng của bạn tạo ra [2, 1, 3](nếu n == 3 làm ví dụ)
dawg

2
@drewk Vâng, sau đó làm cho nó ids = np.array(avgDists).argsort()[-n:][::-1]. Điều cần tránh là tạo một bản sao của toàn bộ danh sách, đó là những gì bạn nhận được khi bạn thêm -vào trước nó. Không liên quan đến ví dụ nhỏ của OP, có thể dành cho các trường hợp lớn hơn.
Jaime

1
@Jaime: Bạn nói đúng. Xem câu trả lời cập nhật của tôi. Cú pháp tho trái ngược với nhận xét của bạn về lát cắt kết thúc: np.array(avgDists).argsort()[::-1][:n]sẽ thực hiện. Ngoài ra, nếu bạn sẽ sử dụng numpy, hãy ở trong numpy. Đầu tiên chuyển đổi danh sách thành một mảng: avgDist=np.array(avgDists)sau đó nó trở thànhavgDist.argsort()[::-1][:n}
dawg

Câu trả lời:


230

Nếu bạn phủ nhận một mảng, các phần tử thấp nhất sẽ trở thành các phần tử cao nhất và ngược lại. Do đó, các chỉ số của các nyếu tố cao nhất là:

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

Một cách khác để lý giải về điều này, như được đề cập trong các ý kiến , là quan sát rằng các yếu tố lớn đang đến cuối cùng trong argsort. Vì vậy, bạn có thể đọc từ phần đuôi của argsort để tìm các nphần tử cao nhất:

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

Cả hai phương thức là O (n log n) về độ phức tạp thời gian, bởi vì argsortcuộc gọi là thuật ngữ chi phối ở đây. Nhưng cách tiếp cận thứ hai có một lợi thế tốt: nó thay thế một phủ định O (n) của mảng bằng một lát O (1) . Nếu bạn đang làm việc với các mảng nhỏ bên trong các vòng lặp thì bạn có thể đạt được một số hiệu suất từ ​​việc tránh sự phủ định đó và nếu bạn đang làm việc với các mảng lớn thì bạn có thể tiết kiệm sử dụng bộ nhớ vì việc phủ định tạo ra một bản sao của toàn bộ mảng.

Lưu ý rằng các phương thức này không phải lúc nào cũng cho kết quả tương đương: nếu yêu cầu triển khai sắp xếp ổn định argsort, ví dụ: bằng cách chuyển đối số từ khóa kind='mergesort', thì chiến lược đầu tiên sẽ duy trì sự ổn định sắp xếp, nhưng chiến lược thứ hai sẽ phá vỡ sự ổn định (nghĩa là các vị trí bằng nhau các mặt hàng sẽ được đảo ngược).

Thời gian ví dụ:

Sử dụng một mảng nhỏ gồm 100 phao và đuôi dài 30, phương thức xem nhanh hơn khoảng 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)

Đối với các mảng lớn hơn, argsort chiếm ưu thế và không có sự khác biệt đáng kể về thời gian

>>> 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)

Xin lưu ý rằng nhận xét từ nedim dưới đây là không chính xác. Việc cắt bớt trước hay sau khi đảo ngược sẽ không có sự khác biệt về hiệu quả, vì cả hai thao tác này chỉ tạo ra một cách nhìn khác nhau về mảng và không thực sự sao chép dữ liệu.


14
Nó thậm chí còn hiệu quả hơn để cắt trước khi đảo ngược, tức là,np.array(avgDists).argsort()[:-n][::-1]
nedim 16/07/2015

3
Những câu trả lời này không tương đương nếu mảng ban đầu chứa nans. Trong trường hợp như vậy, giải pháp đầu tiên dường như cho kết quả tự nhiên hơn với các nans ở cuối thay vì ở đầu.
feilchenfeldt

1
Làm thế nào để so sánh khi một loại ổn định là mong muốn? Có lẽ chiến lược cắt lát đảo ngược các mặt hàng bằng nhau?
Eric

1
@ user3666197 Tôi cảm thấy nó không liên quan đến câu trả lời. Cho dù phủ định có tạo ra một bản sao hay không (nó) không thực sự quan trọng ở đây, thông tin liên quan là việc tính toán phủ định là độ phức tạp O (n) so với lấy một lát cắt khác là O (1) .
wim

1
@ user3666197 Vâng, đó là một điểm tốt - nếu một mảng chiếm 50% bộ nhớ khả dụng, chúng tôi chắc chắn sẽ muốn tránh sao chép nó và gây ra sự tráo đổi. Tôi sẽ chỉnh sửa một lần nữa để đề cập rằng một bản sao được tạo ra ở đó.
wim

70

Giống như Python, trong đó [::-1]đảo ngược mảng được trả về argsort()[:n]đưa ra n phần tử cuối cùng:

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

Ưu điểm của phương pháp này idsquan điểm của avgDists:

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

('OWNDATA' là Sai cho biết đây là chế độ xem, không phải bản sao)

Một cách khác để làm điều này là một cái gì đó như:

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

Vấn đề là cách thức hoạt động của nó là tạo ra âm của từng phần tử trong mảng:

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

ANd tạo một bản sao để làm như vậy:

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

Vì vậy, nếu bạn có thời gian, với bộ dữ liệu rất nhỏ này:

>>> 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

Phương thức xem nhanh hơn đáng kể (và sử dụng 1/2 bộ nhớ ...)


4
Câu trả lời này là tốt, nhưng tôi cảm thấy từ ngữ của bạn diễn đạt sai các đặc điểm hiệu suất thực sự: "ngay cả với tập dữ liệu rất nhỏ này, phương thức xem nhanh hơn đáng kể" . Trong thực tế, phủ định là O (n) và argsort là O (n log n) . Điều này có nghĩa là sự khác biệt về thời gian sẽ giảm đi đối với các tập dữ liệu lớn hơn - thuật ngữ O (n log n) chiếm ưu thế, tuy nhiên đề xuất của bạn là tối ưu hóa phần O (n) . Vì vậy, độ phức tạp vẫn giữ nguyên và đối với tập dữ liệu nhỏ này, đặc biệt là chúng ta thấy bất kỳ sự khác biệt đáng kể nào.
wim

2
Độ phức tạp tương đương không có triệu chứng vẫn có thể có nghĩa là một thuật toán có tốc độ nhanh gấp hai lần so với thuật toán khác. Vứt bỏ sự phân biệt như vậy có thể có hậu quả. Ví dụ: ngay cả khi chênh lệch thời gian (tính theo phần trăm) không đạt tới 0, tôi sẵn sàng đặt cược rằng thuật toán phủ định vẫn sử dụng bộ nhớ gấp đôi.
lỗi

@ bọ Có thể, nhưng nó không có trong trường hợp này. Tôi đã thêm một số thời gian vào câu trả lời của tôi. Các con số cho thấy đối với các mảng lớn hơn, các cách tiếp cận này có thời gian tương tự, điều này hỗ trợ cho giả thuyết rằng argsort chiếm ưu thế. Để phủ nhận, tôi sẽ đoán bạn đúng về việc sử dụng bộ nhớ, nhưng người dùng vẫn có thể thích điều đó nếu họ quan tâm đến vị trí của nan và / hoặc cần một loại ổn định.
Wim

6

Bạn có thể sử dụng các lệnh lật numpy.flipud()hoặc numpy.fliplr()để lấy các chỉ mục theo thứ tự giảm dần sau khi sắp xếp bằng argsortlệnh. Đó là những gì tôi thường làm.


Điều đó chậm hơn nhiều so với việc cắt stackoverflow.com/a/44921013/125507
endolith

5

Thay vì sử dụng, np.argsortbạn có thể sử dụng np.argpartition- nếu bạn chỉ cần các chỉ số của các phần tử n thấp nhất / cao nhất.

Điều đó không yêu cầu sắp xếp toàn bộ mảng mà chỉ là phần bạn cần nhưng lưu ý rằng "thứ tự bên trong phân vùng của bạn" không được xác định, vì vậy trong khi nó đưa ra các chỉ số chính xác thì chúng có thể không được sắp xếp chính xác:

>>> 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)

Hoặc, nếu bạn đang sử dụng cả hai cùng nhau, đó là argsort và argpartition, thao tác phải được thực hiện trên hoạt động argpartition.
demongolem

3

Bạn có thể tạo một bản sao của mảng và sau đó nhân từng phần tử với -1.
Như một hiệu ứng, các yếu tố trước lớn nhất sẽ trở thành nhỏ nhất.
Phân của n phần tử nhỏ nhất trong bản sao là n phần tử lớn nhất trong bản gốc.


điều này được thực hiện dễ dàng phủ nhận mảng, như đã nêu trong các câu trả lời khác:-array
onofricamila

2

Như @Kanmani gợi ý, có thể sử dụng cách diễn giải dễ dàng hơn numpy.flip, như sau:

import numpy as np

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

Bằng cách sử dụng mẫu khách truy cập thay vì các hàm thành viên, việc đọc thứ tự các thao tác sẽ dễ dàng hơn.


1

Với ví dụ của bạn:

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

Lấy chỉ số của n giá trị tối đa:

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

Sắp xếp chúng theo thứ tự giảm dần:

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

Lấy kết quả (cho n = 4):

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

-1

Một cách khác là chỉ sử dụng một '-' trong đối số cho argsort như trong: "df [np.argsort (-df [:, 0])]", với điều kiện df là khung dữ liệu và bạn muốn sắp xếp nó theo thứ nhất cột (được biểu thị bằng số cột '0'). Thay đổi tên cột cho phù hợp. Tất nhiên, cột phải là một số.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.