Maneira mais rápida de processar numericamente matriz 2D: dataframe vs série vs matriz vs numba

Editar para adicionar: Eu não acho que os valores de referência da numba sejam justos, observa abaixo

Estou tentando comparar diferentes abordagens para processar dados numericamente para o seguinte caso de uso:

Conjunto de dados bastante grande (mais de 100.000 registros)Mais de 100 linhas de código bastante simples (z = x + y)Não precisa classificar ou indexar

Em outras palavras, a generalidade total de séries e quadros de dados não é necessária, embora eles sejam incluídos aqui porque ainda são maneiras convenientes de encapsular os dados e geralmente há pré ou pós-processamento que requer a generalidade dos pandas sobre matrizes numpy.

Pergunta, questão: Com base nesse caso de uso, os seguintes parâmetros de referência são adequados e, se não, como posso melhorá-los?

# importing pandas, numpy, Series, DataFrame in standard way
from numba import jit
nobs = 10000
nlines = 100

def proc_df():
   df = DataFrame({ 'x': np.random.randn(nobs),
                    'y': np.random.randn(nobs) })
   for i in range(nlines):
      df['z'] = df.x + df.y
   return df.z

def proc_ser():
   x = Series(np.random.randn(nobs))
   y = Series(np.random.randn(nobs))
   for i in range(nlines):
      z = x + y
   return z

def proc_arr():
   x = np.random.randn(nobs)
   y = np.random.randn(nobs)
   for i in range(nlines):
      z = x + y
   return z

@jit
def proc_numba():
   xx = np.random.randn(nobs)
   yy = np.random.randn(nobs)
   zz = np.zeros(nobs)
   for j in range(nobs):
      x, y = xx[j], yy[j]
      for i in range(nlines):
         z = x + y
      zz[j] = z
   return zz

Resultados (estação de trabalho Xeon de 7 anos e 3 anos de idade (quad-core). Distribuição anaconda padrão e recente ou muito próxima.)

In [1251]: %timeit proc_df()
10 loops, best of 3: 46.6 ms per loop

In [1252]: %timeit proc_ser()
100 loops, best of 3: 15.8 ms per loop

In [1253]: %timeit proc_arr()
100 loops, best of 3: 2.02 ms per loop

In [1254]: %timeit proc_numba()
1000 loops, best of 3: 1.04 ms per loop   # may not be valid result (see note below)

Editar para adicionar (resposta a jeff) resultados alternativos de passar df / series / array para funções em vez de criá-los dentro de funções (ou seja, mover as linhas de código que contêm 'randn' da função interna para a função externa):

10 loops, best of 3: 45.1 ms per loop
100 loops, best of 3: 15.1 ms per loop
1000 loops, best of 3: 1.07 ms per loop
100000 loops, best of 3: 17.9 µs per loop   # may not be valid result (see note below)

Nota sobre os resultados numba: Eu acho que o compilador numba deve otimizar o loop for e reduzir o loop for a uma única iteração. Não sei disso, mas é a única explicação que posso encontrar, pois não poderia ser 50x mais rápido do que entorpecido, certo? Pergunta de acompanhamento aqui:Por que numba é mais rápido que numpy aqui?

questionAnswers(2)

yourAnswerToTheQuestion