Có tương đương với `sum ()` dựng sẵn sử dụng phép gán tăng không?


8

Có thư viện chuẩn / numpy tương đương với chức năng sau không:

def augmented_assignment_sum(iterable, start=0):
    for n in iterable:
        start += n
    return start

?

Mặc dù sum(ITERABLE)rất thanh lịch, nó sử dụng +toán tử thay vì +=, trong trường hợp các np.ndarrayđối tượng có thể ảnh hưởng đến hiệu suất.

Tôi đã kiểm tra rằng chức năng của tôi có thể nhanh như vậy sum()(trong khi việc sử dụng tương đương của nó +chậm hơn nhiều). Vì nó là một hàm Python thuần túy, tôi đoán hiệu năng của nó vẫn bị khuyết tật, do đó tôi đang tìm kiếm một số thay thế:

In [49]: ARRAYS = [np.random.random((1000000)) for _ in range(100)]

In [50]: def not_augmented_assignment_sum(iterable, start=0): 
    ...:     for n in iterable: 
    ...:         start = start + n 
    ...:     return start 
    ...:                                                                                                                                                                                                                                                                       

In [51]: %timeit not_augmented_assignment_sum(ARRAYS)                                                                                                                                                                                                                          
63.6 ms ± 8.88 ms per loop (mean ± std. dev. of 7 runs, 10 loops each)

In [52]: %timeit sum(ARRAYS)                                                                                                                                                                                                                                                   
31.2 ms ± 2.18 ms per loop (mean ± std. dev. of 7 runs, 10 loops each)

In [53]: %timeit augmented_assignment_sum(ARRAYS)                                                                                                                                                                                                                              
31.2 ms ± 4.73 ms per loop (mean ± std. dev. of 7 runs, 10 loops each)

In [54]: %timeit not_augmented_assignment_sum(ARRAYS)                                                                                                                                                                                                                          
62.5 ms ± 12.1 ms per loop (mean ± std. dev. of 7 runs, 10 loops each)

In [55]: %timeit sum(ARRAYS)                                                                                                                                                                                                                                                   
37 ms ± 9.51 ms per loop (mean ± std. dev. of 7 runs, 10 loops each)

In [56]: %timeit augmented_assignment_sum(ARRAYS)                                                                                                                                                                                                                              
27.7 ms ± 2.53 ms per loop (mean ± std. dev. of 7 runs, 10 loops each)

Tôi đã cố gắng sử dụng functools.reducekết hợp với operator.iadd, nhưng hiệu suất của nó là tương tự:

In [79]: %timeit reduce(iadd, ARRAYS, 0)                                                                                                                                                                                                                                       
33.4 ms ± 11.6 ms per loop (mean ± std. dev. of 7 runs, 10 loops each)

In [80]: %timeit reduce(iadd, ARRAYS, 0)                                                                                                                                                                                                                                       
29.4 ms ± 2.31 ms per loop (mean ± std. dev. of 7 runs, 10 loops each)

Tôi cũng quan tâm đến hiệu quả bộ nhớ, do đó thích các bài tập tăng cường vì chúng không yêu cầu tạo các đối tượng trung gian.


np.add.reduce(ARRAYS)?
Dani Mesejo

1
@DanielMesejo thật đáng buồn 374 ms ± 83.4 ms per loop (mean ± std. dev. of 7 runs, 1 loop each):-( Mặc dù nó nhanh hơn đáng kể nếu ARRAYSlà mảng 2D.
abukaj

Ngoài ra còn có numpy.sum
Dani Mesejo

@DanielMesejo Nó trả về vô hướng trừ khi được gọi với axis=0. Sau đó, phải mất 355 ms ± 16.4 ms per loop (mean ± std. dev. of 7 runs, 1 loop each):-( Trong nội bộ, nó sử dụng np.add.reduce()(numpy v. 1.15.4)
abukaj

Thế còn a np.dot(your_array, np.ones(len(your_array))). Nên chuyển sang BLAS và nhanh chóng hợp lý.
dùng228395

Câu trả lời:


2

Câu trả lời cho câu hỏi tiêu đề --- Tôi hy vọng @Martijn Pieters sẽ tha thứ cho sự lựa chọn ẩn dụ của tôi --- ngay từ miệng con ngựa là: Không, không có nội dung như vậy.

Nếu chúng ta cho phép một vài dòng mã thực hiện tương đương như vậy, chúng ta sẽ có được một bức tranh khá phức tạp với những gì nhanh nhất rất nhiều tùy thuộc vào kích thước toán hạng:

nhập mô tả hình ảnh ở đây

Biểu đồ này hiển thị thời gian của các phương thức khác nhau liên quan đến sumkích thước toán hạng, số lượng thuật ngữ luôn là 100. augmented_assignment_sumbắt đầu trả cho các kích thước toán hạng tương đối lớn. Sử dụng có scipy.linalg.blas.*axpyvẻ khá cạnh tranh so với hầu hết các phạm vi được thử nghiệm, nhược điểm chính của nó là cách dễ sử dụng hơn sum.

Mã số:

from simple_benchmark import BenchmarkBuilder, MultiArgument
import numpy as np
from scipy.linalg import blas

B = BenchmarkBuilder()

@B.add_function()
def augmented_assignment_sum(iterable, start=0):
    for n in iterable:
        start += n
    return start

@B.add_function()
def not_augmented_assignment_sum(iterable, start=0):
    for n in iterable:
        start = start + n
    return start

@B.add_function()
def plain_sum(iterable, start=0):
    return sum(iterable,start)

@B.add_function()
def blas_sum(iterable, start=None):
    iterable = iter(iterable)
    if start is None:
        try:
            start = next(iterable).copy()
        except StopIteration:
            return 0
    try:
        f = {np.dtype('float32'):blas.saxpy,
             np.dtype('float64'):blas.daxpy,
             np.dtype('complex64'):blas.caxpy,
             np.dtype('complex128'):blas.zaxpy}[start.dtype]
    except KeyError:
        f = blas.daxpy
        start = start.astype(float)
    for n in iterable:
        f(n,start)
    return start

@B.add_arguments('size of terms')
def argument_provider():
    for exp in range(1,21):
        sz = int(2**exp)
        yield sz,[np.random.randn(sz) for _ in range(100)]

r = B.run()
r.plot(relative_to=plain_sum)

import pylab
pylab.savefig('inplacesum.png')

Tôi biết, về mặt kỹ thuật không phải là một câu trả lời cho tiêu đề, nhưng tôi cho rằng đây là điều mà OP quan tâm.
Paul Panzer

1
Chỉ có một điều còn thiếu để trả lời câu hỏi tiêu đề: một tuyên bố rằng tôi không có chức năng nào như vậy. ;)
abukaj

1
@abukaj: không có chức năng như vậy.
Martijn Pieters

1
@MartijnPieters Điều đó có thể giải thích tại sao tôi không tìm thấy. ;)
abukaj
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.