Какое лучшее решение на pythonic?

#python #numpy

#python #numpy

Вопрос:

Поскольку X имеет форму массива (n, m), а Y — список с длиной = n, значения которого являются двоичными, какова наилучшая альтернатива pythonic для следующего кода с использованием numpy?

 p1 = np.zeros(X.shape[1])
p0 = np.zeros(X.shape[1])
for i in range(len(X[0])):        
        sum_1 = np.where(Y==1,X[:,i],0).sum()
        sum_0 = np.where(Y==0,X[:,i],0).sum()
        p1[i] = sum_1
        p0[i] = sum_0
    
  

Комментарии:

1. Какова цель этого кода? У вас есть пример для X and Y ?

2. X — это матрица из n строк (выборок) и m столбцов (атрибутов). Y — это вектор с классами (1 или 0) каждого образца X. Мне нужно заполнить p1 суммой каждого атрибута X, когда Y = 1, и p0 суммой каждого атрибута X, когда Y = 0.

Ответ №1:

Вот более быстрая и простая версия:

 p1 = X.T @ Y # or np.dot(X.T, Y) if on Python < 3.5
p0 = X.T @ (1 - Y)
  

Это использует тот факт, что ваш Y массив состоит из нулей и единиц, и вычисляет быстрое скалярное произведение.


Синхронизация результатов со следующей структурой:

 import numpy as np

n = 2000
m = 1000
X = np.random.random((n, m))
Y = (np.random.random((n,)) > 0.5).astype(int)

def v0():
    p1 = np.zeros(X.shape[1])
    p0 = np.zeros(X.shape[1])
    for i in range(len(X[0])):
        sum_1 = np.where(Y==1,X[:,i],0).sum()
        sum_0 = np.where(Y==0,X[:,i],0).sum()
        p1[i] = sum_1
        p0[i] = sum_0
    return p0, p1

def v1():
    p1 = np.sum(X[np.where(Y==1)], axis=0)
    p0 = np.sum(X[np.where(Y==0)], axis=0)
    return p0, p1

def v2():
    p1 = X.T @ Y # or np.dot(X.T, Y) if on Python < 3.5
    p0 = X.T @ (1 - Y)
    return p0, p1

p0_0, p1_0 = v0()
p0_1, p1_1 = v1()
p0_2, p1_2 = v2()
assert np.allclose(p0_0, p0_1)
assert np.allclose(p0_0, p0_2)
assert np.allclose(p1_0, p1_1)
assert np.allclose(p1_0, p1_2)
  
 $ python3 -m timeit -s 'import test' 'test.v0()'
10 loops, best of 5: 33.5 msec per loop
$ python3 -m timeit -s 'import test' 'test.v1()'
100 loops, best of 5: 3.81 msec per loop
$ python3 -m timeit -s 'import test' 'test.v2()'
500 loops, best of 5: 794 usec per loop
  

Эта версия более чем в 40 раз быстрее, чем ваш оригинал для этого набора размеров.

Комментарии:

1. Действительно, очень эффективно. Большое вам спасибо.

Ответ №2:

Вы суммируете по первой оси X при некотором условии для строк

 p1 = np.sum(X[np.where(Y==1)], axis=0)
p0 = np.sum(X[np.where(Y==0)], axis=0)
  

Ответ №3:

Если Y это логическая область, вы можете использовать Y ее непосредственно в качестве индекса, а numpy маскирует соответствующие строки. Вы также можете отрицать Y с ~Y помощью, чтобы получить другие строки:

 >>> X
array([[1, 2],
       [2, 3],
       [3, 4]])
>>> Y
array([False, False,  True])
>>> X[Y]
array([[3, 4]])
>>> X[~Y]
array([[1, 2],
       [2, 3]])
>>> X[Y].sum(axis=0)
array([3, 4])
>>> X[~Y].sum(axis=0)
array([3, 5])