2010-07-31 30 views
17

numpy.vectorize lấy hàm f: a-> b và biến nó thành g: a [] -> b [].Sử dụng Numpy Vectorize trên các hàm trả về các vectơ

Điều này hoạt động tốt khi ab là vô hướng, nhưng tôi không thể nghĩ ra lý do tại sao nó không hoạt động với b dưới dạng ndarray hoặc danh sách, tức là f: a-> b [] và g: a [] -> b [] []

Ví dụ:

import numpy as np 
def f(x): 
    return x * np.array([1,1,1,1,1], dtype=np.float32) 
g = np.vectorize(f, otypes=[np.ndarray]) 
a = np.arange(4) 
print(g(a)) 

sản lượng này:

array([[ 0. 0. 0. 0. 0.], 
     [ 1. 1. 1. 1. 1.], 
     [ 2. 2. 2. 2. 2.], 
     [ 3. 3. 3. 3. 3.]], dtype=object) 

Ok, do đó cung cấp cho các giá trị đúng, nhưng dtype sai. Và thậm chí tệ hơn:

g(a).shape 

sản lượng:

(4,) 

Vì vậy, mảng này là khá nhiều vô ích. Tôi biết tôi có thể chuyển đổi nó thực hiện:

np.array(map(list, a), dtype=np.float32) 

để cho tôi những gì tôi muốn:

array([[ 0., 0., 0., 0., 0.], 
     [ 1., 1., 1., 1., 1.], 
     [ 2., 2., 2., 2., 2.], 
     [ 3., 3., 3., 3., 3.]], dtype=float32) 

nhưng điều đó không phải là hiệu quả cũng không pythonic. Có ai trong số các bạn tìm cách dọn dẹp được không?

Cảm ơn trước!

Trả lời

24

np.vectorize chỉ là một chức năng tiện lợi. Nó không thực sự là make code run any faster. Nếu không thuận tiện khi sử dụng np.vectorize, chỉ cần viết chức năng của riêng bạn hoạt động như bạn muốn.

Mục đích của np.vectorize là chuyển đổi các chức năng không nhận thức được (ví dụ: lấy phao làm đầu vào và trả về nổi) thành các hàm có thể hoạt động trên (và trả về) mảng cố định.

Chức năng của bạn f đã được nhận biết rõ ràng - nó sử dụng mảng có nhiều mảng trong định nghĩa của nó và trả về mảng có nhiều mảng. Vì vậy, np.vectorize không phù hợp với trường hợp sử dụng của bạn.

Giải pháp do đó chỉ là để cuộn chức năng của riêng bạn f hoạt động theo cách bạn mong muốn.

+6

Thật vậy, "chỉ là một chức năng tiện lợi" mô tả hầu hết API numpy. Đó là toàn bộ vấn đề. Nó quá xấu chức năng này không hoạt động như một trong những mong đợi. –

+1

Hầu hết các chức năng NumPy chỉ chậm hơn một chút so với hàm tương đương được viết bằng C. Điều này đúng khi hàm NumPy chỉ đơn thuần là một trình bao bọc mỏng xung quanh một hàm C (hoặc Fortran). Ngược lại, một hàm 'np.vectorized' vẫn phải gọi hàm * Python * một lần cho mỗi phần tử trong mảng, do đó nó thực hiện giống mã Python hơn mã C. Tra cứu tên động của Python cung cấp sự linh hoạt hơn, nhưng có thể chậm hơn nhiều so với mã C. – unutbu

+0

không theo https://stackoverflow.com/questions/35215161/most-efficient-way-to-map-function-over-numpy-array –

2
import numpy as np 
def f(x): 
    return x * np.array([1,1,1,1,1], dtype=np.float32) 
g = np.vectorize(f, otypes=[np.ndarray]) 
a = np.arange(4) 
b = g(a) 
b = np.array(b.tolist()) 
print(b)#b.shape = (4,5) 
c = np.ones((2,3,4)) 
d = g(c) 
d = np.array(d.tolist()) 
print(d)#d.shape = (2,3,4,5) 

Điều này sẽ khắc phục sự cố và nó sẽ hoạt động bất kể kích thước đầu vào của bạn là gì. "bản đồ" chỉ hoạt động cho một đầu vào nhỏ. Sử dụng ".tolist()" và tạo một giải thuật mới giải quyết vấn đề hoàn toàn và độc đáo hơn (tôi tin). Hi vọng điêu nay co ich.

0

Cách tốt nhất để giải quyết vấn đề này là sử dụng mảng 2-D NumPy (trong trường hợp này là mảng cột) làm đầu vào cho hàm gốc, sau đó sẽ tạo ra kết quả 2-D với kết quả Tôi tin rằng bạn đang mong đợi.

Đây là những gì nó có thể trông giống như trong mã:

import numpy as np 
def f(x): 
    return x*np.array([1, 1, 1, 1, 1], dtype=np.float32) 

a = np.arange(4).reshape((4, 1)) 
b = f(a) 
# b is a 2-D array with shape (4, 5) 
print(b) 

Đây là một cách đơn giản hơn nhiều và ít bị lỗi để hoàn thành các hoạt động.Thay vì cố gắng chuyển đổi hàm bằng numpy.vectorize, phương thức này dựa vào khả năng tự nhiên của các mảng phát sóng của NumPy. Bí quyết là đảm bảo rằng ít nhất một chiều có chiều dài bằng nhau giữa các mảng.

1

Tôi đã viết một chức năng, có vẻ như nó phù hợp với nhu cầu của bạn.

def amap(func, *args): 
    '''array version of build-in map 
    amap(function, sequence[, sequence, ...]) -> array 
    Examples 
    -------- 
    >>> amap(lambda x: x**2, 1) 
    array(1) 
    >>> amap(lambda x: x**2, [1, 2]) 
    array([1, 4]) 
    >>> amap(lambda x,y: y**2 + x**2, 1, [1, 2]) 
    array([2, 5]) 
    >>> amap(lambda x: (x, x), 1) 
    array([1, 1]) 
    >>> amap(lambda x,y: [x**2, y**2], [1,2], [3,4]) 
    array([[1, 9], [4, 16]]) 
    ''' 
    args = np.broadcast(None, *args) 
    res = np.array([func(*arg[1:]) for arg in args]) 
    shape = args.shape + res.shape[1:] 
    return res.reshape(shape) 

Hãy thử

def f(x): 
     return x * np.array([1,1,1,1,1], dtype=np.float32) 
amap(f, np.arange(4)) 

Đầu ra

array([[ 0., 0., 0., 0., 0.], 
     [ 1., 1., 1., 1., 1.], 
     [ 2., 2., 2., 2., 2.], 
     [ 3., 3., 3., 3., 3.]], dtype=float32) 

Bạn cũng có thể quấn nó với lambda hoặc một phần để thuận tiện

g = lambda x:amap(f, x) 
g(np.arange(4)) 

Lưu ý docstring của vectorize nói

Chức năng vectorize được cung cấp chủ yếu cho thuận tiện, không phải cho hiệu suất. Việc thực hiện về cơ bản là một vòng lặp for.

Vì vậy, chúng tôi mong đợi amap ở đây có hiệu suất tương tự như vectorize. Tôi đã không kiểm tra nó, bất kỳ kiểm tra hiệu suất được chào đón.

Nếu hiệu suất thực sự quan trọng, bạn nên cân nhắc điều gì đó khác, ví dụ: tính toán mảng trực tiếp với reshapebroadcast để tránh vòng lặp trong trăn thuần túy (cả hai vectorizeamap là trường hợp sau).

1

Thông số mới signature trong 1.12.0 thực hiện chính xác những gì bạn làm gì.

def f(x): 
    return x * np.array([1,1,1,1,1], dtype=np.float32) 

g = np.vectorize(f, signature='()->(n)') 

Sau đó, g(np.arange(4)).shape sẽ cung cấp (4L, 5L).

Ở đây, chữ ký của f được chỉ định. (n) là hình dạng của giá trị trả lại và () là hình dạng của thông số là vô hướng. Và các tham số có thể là mảng quá. Để có chữ ký phức tạp hơn, hãy xem Generalized Universal Function API.

Các vấn đề liên quan