728x90
728x90
728x90

빠른 이유로는 3가지 이유가 있다.

1. numpy.ndarray는 a collection of similar data-types that are densely packed in memory.

(반면, list는 different data-types을 가질 수 있고 computation하는 데에 있어서 몇가지 과정을 더 타야한다.)

(이 부분은 하단의 설명을 다시 보자.)

2. numpy는 한 task를 subtask로 알아서 나눠서 parallely하게 작동하기도 한다.

(예를 들면, np.dot()을 수행할 때, argument의 size가 크면 cpu core 전부를 쓰는 것을 확인할 수 있다.)

3. numpy는 C언어로 구현되어 있어서 더 빠르게 작동한다.

 

 

하늘색이 실제 저장된 값

ndarray는 element조회시 "data"에 접근 후, 모든 데이터를 쭉 접근 가능

쭉이란, 각 값들이 메모리에 연속적으로 저장되어 있음

게다가 각 element가 같은 dtype이라, +N byte형태로 빠른 element 연속 접근이 가능

 

list의 경우 각 파란색 값이 메모리에 연속적으로 존재하지 않음

ob_item 내에 각 element의 reference(메모리 주소)를 갖고 있다.

그 reference를 타고 가더라도, 객체 자체가 있고, 그 객체 내에 ob_digit(객체가 int라면)로 가야 element에 접근

즉, 접근단계가 ndarray(1)에 비해 list(3)가 접근 단계가 많다.

그리고 next element에 접근할 때도 ndarray(1)인데 list(3)이므로 접근 방식 자체에서 느린 구조이다.

 

결론

ndarray는

dtype이 similar한 녀석들로 만들면 고속 접근이 가능

 

list는

dtype이 different하더라도 담을 수가 있음, 따라서 무한한 정수가 가능(담긴 element int가 아무리 커져도 int32 형태로 제한이 걸릴 일은 없다는 것)

 

 

참고자료:

towardsdatascience.com/how-fast-numpy-really-is-e9111df44347

 

How Fast Numpy Really is and Why?

A comparison with standard Python Lists.

towardsdatascience.com

spyhce.com/blog/cpython-data-structures

 

CPython data structures | Spyhce blog

In this article we have a look at the underlying C implementation, how these types work and what tweaks are there to make them faster. Learn more!

spyhce.com

www.youtube.com/watch?v=fiYD0yCou4k

 

728x90
728x90

상황:

a:numpy.ndarray, 2-dimensional, 

a의 각 row마다 k개의 top values뽑기

 

ex)

a=np.random.randint(0,10,(4,5))  # 0에서 9까지 values중 random integers뽑아서 shape이 (4,5)인 것

k=3

res = a[np.arange(len(a))[:, None], np.argsort(a)[:, -k:]][:, ::-1]

 

설명:

(1)

np.argsort는 ascending order로 indices를 뽑아냄

 

(2)

the smallest top k를 구하려면 np.argsort(a)[:, :k], 그리고 뒤에 [:, ::-1] 없어야함

 

(3) 

만약 the top k largest/smallest values 를 ordering없이 뽑으려면

np.argsort보다 np.partition이 낫다. 왜냐하면 np.argsort는 sorting(O(n*log(n))을 하지만 후자는 sorting하지 않음(O(n))

즉, 

res = np.partition(a, -k)[:, -k:] 하면은 top k values(not ordered)를 얻을 수 있다.

 

(4)

the top k largest/smallest values의 indices를 ordering없이 뽑으려면

np.partition말고 np.argpartition을 활용하면 된다.

즉,

res = np.argparition(a, -k)[:, -k:]

 

(5)

a에 negation붙여서 하진 말 것, 그러면 new object를 만들어서 하기 때문에 memory 더 쓰게 됨

 

참고자료:

kanoki.org/2020/01/14/find-k-smallest-and-largest-values-and-its-indices-in-a-numpy-array/

 

Find K smallest and largest values and its indices in a numpy array

To find the maximum and minimum value in an array you can use numpy argmax and argmin function

kanoki.org

 

728x90
728x90

상황:

a = np.arange(20).reshape(4,5)

b = np.array([[0,1],[1,2],[2,3],[3,4]])

a의 first row에서는 [0,1]에 해당하는 entries를

a의 second row에서는 [1,2]에 해당하는 entries를

a의 third row에서는 [2,3]에 해당하는 entries를

a의 fourth row에서는 [3,4]에 해당하는 entries를 

가져오고 싶다.

 

res = a[np.arange(4)[:, None], b]

 

설명 

(1)

numpy.ndarray에 [:, None]을 하면 길이가 1인 axis를 하나 더 생성한다.

예를 들면

q = np.arange(10)  # shape은 (10,)

w = q[:, None]  # shape은 (10,1)

e = q[:, None]  # shape은 (10, 1, 1)

 

reshape(-1,1)과 비슷해보이지만

w = q.reshape(-1,1)  # (10,1)

e = w.reshape(-1,1)  # (10,1), 즉 w와 shape이 같음

 

(2)

a의 third row 빼고 가져오고 싶다면

ind = np.array([0,1,3])

res = a[ind[:,None], b[ind]]

 

(3)

a의 first, third빼고 모두 가져오고 싶다면

ind = np.array([x for x in range(len(a)) if x not in [0,2]])

res = a[ind[:,None], b[ind]]

 

728x90
728x90

상황:

arr는 2d numpy.ndarray

dict_는 dictionary, arr의 각 원소를 변환시킬 mapping

 

1.

res = np.vectorize(dict_.__getitem__)(arr)

np.vectorize는 내부적으로는 결국 python loop방식으로 돌기 때문에 numpy의 C base speedup 이점을 얻기 힘들다.

 

2.

u, inv = np.unique(arr, return_inverse=True)

res = np.array([dict_[x] for x in u])[inv].reshape(a.shape)

이는 [inv]와 reshape에서 numpy의 speedup이점을 얻을 수가 있다.

 

3. 만약 dict_의 key에 arr에 특정 원소만 존재한다면?

 

(1) arr의 일부 원소는 변환하고, 일부는 default값으로 바꿔 넣고 싶다면

u, inv = np.unique(arr, return_inverse=True)

res = np.array([dict_.get(x, "DEFAULT") for x in u])[inv].reshape(a.shape)

이렇게 dict_의 get method로 변환하고, missing key에 대해선 default 입력 가능

 

(2) arr의 일부 원소는 변환하고, 일부는 원래 그대로 두고 싶다면

u, inv = np.unique(arr, return_inverse=True)

res = np.array([dict_.get(x, x) for x in u])[inv].reshape(a.shape)

이렇게 dict_의 get method로 변환하고, missing key에 대해선 원본 그대로 입력 가능

728x90
728x90

np.select(condition_list, choice_list, default)

 

x = np.arange(10)

>>> condlist = [x<3, x>5]

>>> choicelist = [x, x**2]

>>> np.select(condlist, choicelist)

array([ 0, 1, 2, 0, 0, 0, 36, 49, 64, 81])

 

 

-condition_list에서 여러개  condition을 만족한다면 first encounter condition에 해당하는 choice를 적용함

-default는 어떠한 condition 도 만족하지 않는 경우 output value

 

ex)

condlist = [

(score_df['average'] >= 90),

(score_df['average'] < 90) & (score_df['average'] >= 80),

(score_df['average'] < 80) & (score_df['average'] >= 70)]

choicelist = ['A','B','C']

 

score_df['Grade'] = np.select(condlist, choicelist)

728x90

+ Recent posts