Je génère une liste de tableaux numpy unidimensionnels dans une boucle et convertis plus tard cette liste en un tableau numpy 2d. J'aurais préalloué un tableau numpy 2d si je connaissais le nombre d'éléments à l'avance, mais je ne le sais pas, donc je mets tout dans une liste.
La maquette est ci-dessous:
>>> list_of_arrays = map(lambda x: x*ones(2), range(5))
>>> list_of_arrays
[array([ 0., 0.]), array([ 1., 1.]), array([ 2., 2.]), array([ 3., 3.]), array([ 4., 4.])]
>>> arr = array(list_of_arrays)
>>> arr
array([[ 0., 0.],
[ 1., 1.],
[ 2., 2.],
[ 3., 3.],
[ 4., 4.]])
Ma question est la suivante:
Existe-t-il un meilleur moyen (en termes de performances) de procéder à la collecte de données numériques séquentielles (dans mon cas, des tableaux numpy) que de les mettre dans une liste, puis d'en faire un tableau numpy (je crée un nouvel obj et copie les données)? Existe-t-il une structure de données matricielle "extensible" disponible dans un module bien testé?
Une taille typique de ma matrice 2d serait comprise entre 100x10 et 5000x10 flotteurs
ÉDITER: Dans cet exemple, j'utilise la carte, mais dans mon application actuelle, j'ai une boucle for
Supposons que vous sachiez que le tableau final arr
ne sera jamais supérieur à 5000x10. Vous pouvez ensuite pré-allouer un tableau de taille maximale, le remplir avec des données tout au long de la boucle, puis utiliser arr.resize
pour le réduire à la taille découverte après avoir quitté la boucle.
Les tests ci-dessous suggèrent que cela sera légèrement plus rapide que la construction de listes intermédiaires python, quelle que soit la taille ultime du tableau.
Aussi, arr.resize
désalloue la mémoire inutilisée, donc l'empreinte mémoire finale (mais peut-être pas intermédiaire) est plus petite que celle utilisée par python_lists_to_array
.
Ceci montre numpy_all_the_way
est plus rapide:
% python -mtimeit -s"import test" "test.numpy_all_the_way(100)"
100 loops, best of 3: 1.78 msec per loop
% python -mtimeit -s"import test" "test.numpy_all_the_way(1000)"
100 loops, best of 3: 18.1 msec per loop
% python -mtimeit -s"import test" "test.numpy_all_the_way(5000)"
10 loops, best of 3: 90.4 msec per loop
% python -mtimeit -s"import test" "test.python_lists_to_array(100)"
1000 loops, best of 3: 1.97 msec per loop
% python -mtimeit -s"import test" "test.python_lists_to_array(1000)"
10 loops, best of 3: 20.3 msec per loop
% python -mtimeit -s"import test" "test.python_lists_to_array(5000)"
10 loops, best of 3: 101 msec per loop
Ceci montre numpy_all_the_way
utilise moins de mémoire:
% test.py
Initial memory usage: 19788
After python_lists_to_array: 20976
After numpy_all_the_way: 20348
test.py:
import numpy as np
import os
def memory_usage():
pid = os.getpid()
return next(line for line in open('/proc/%s/status' % pid).read().splitlines()
if line.startswith('VmSize')).split()[-2]
N, M = 5000, 10
def python_lists_to_array(k):
list_of_arrays = list(map(lambda x: x * np.ones(M), range(k)))
arr = np.array(list_of_arrays)
return arr
def numpy_all_the_way(k):
arr = np.empty((N, M))
for x in range(k):
arr[x] = x * np.ones(M)
arr.resize((k, M))
return arr
if __== '__main__':
print('Initial memory usage: %s' % memory_usage())
arr = python_lists_to_array(5000)
print('After python_lists_to_array: %s' % memory_usage())
arr = numpy_all_the_way(5000)
print('After numpy_all_the_way: %s' % memory_usage())
Manière pratique, en utilisant numpy.concatenate
. Je pense que c'est aussi plus rapide que la réponse de @ unutbu:
In [32]: import numpy as np
In [33]: list_of_arrays = list(map(lambda x: x * np.ones(2), range(5)))
In [34]: list_of_arrays
Out[34]:
[array([ 0., 0.]),
array([ 1., 1.]),
array([ 2., 2.]),
array([ 3., 3.]),
array([ 4., 4.])]
In [37]: shape = list(list_of_arrays[0].shape)
In [38]: shape
Out[38]: [2]
In [39]: shape[:0] = [len(list_of_arrays)]
In [40]: shape
Out[40]: [5, 2]
In [41]: arr = np.concatenate(list_of_arrays).reshape(shape)
In [42]: arr
Out[42]:
array([[ 0., 0.],
[ 1., 1.],
[ 2., 2.],
[ 3., 3.],
[ 4., 4.]])
Encore plus simple que la réponse de @Gill Bates, voici un code d'une ligne:
np.stack(list_of_arrays, axis=0)
J'ajouterai ma propre version de la réponse de ~ unutbu. Similaire à numpy_all_the, mais vous redimensionnez dynamiquement si vous avez une erreur d'index. Je pensais que cela aurait été un peu plus rapide pour les petits ensembles de données, mais c'est un peu plus lent - la vérification des limites ralentit trop les choses.
initial_guess = 1000
def my_numpy_all_the_way(k):
arr=np.empty((initial_guess,M))
for x,row in enumerate(make_test_data(k)):
try:
arr[x]=row
except IndexError:
arr.resize((arr.shape[0]*2, arr.shape[1]))
arr[x]=row
arr.resize((k,M))
return arr
Réponse @fnjn encore plus simple
np.vstack(list_of_arrays)
Ce que vous faites est la méthode standard. Une propriété des tableaux numpy est qu'ils ont besoin d'une mémoire contiguë. La seule possibilité de "trous" à laquelle je peux penser est possible avec le membre strides
de PyArrayObject
, mais cela n'affecte pas la discussion ici. Étant donné que les tableaux numpy ont une mémoire contiguë et sont "préalloués", ajouter une nouvelle ligne/colonne signifie allouer de la nouvelle mémoire, copier des données, puis libérer l'ancienne mémoire. Si vous faites beaucoup cela, ce n'est pas très efficace.
Un cas où quelqu'un pourrait ne pas vouloir créer une liste puis la convertir en un tableau numpy à la fin est lorsque la liste contient beaucoup de nombres: un tableau numpy de nombres prend beaucoup moins d'espace qu'un natif Python liste de nombres (puisque la liste native Python stocke Python objets). Pour vos tailles de tableau typiques, je ne pense pas que ce soit un problème.
Lorsque vous créez votre tableau final à partir d'une liste de tableaux, vous copiez toutes les données vers un nouvel emplacement pour le nouveau (2D dans votre exemple) ) tableau. C'est toujours beaucoup plus efficace que d'avoir un tableau numpy et de faire next = numpy.vstack((next, new_row))
chaque fois que vous obtenez de nouvelles données. vstack()
copiera toutes les données pour chaque "ligne".
Il y avait un fil sur la liste de diffusion numpy-discussion il y a quelque temps qui discutait de la possibilité d'ajouter un nouveau type de tableau numpy qui permet une extension/ajout efficace. Il semble qu'il y ait eu un intérêt important à ce moment-là, même si je ne sais pas si quelque chose en est sorti. Vous voudrez peut-être regarder ce fil.
Je dirais que ce que vous faites est très Pythonique et efficace, donc à moins d'avoir vraiment besoin d'autre chose (plus d'espace, peut-être?), Vous devriez être d'accord. C'est ainsi que je crée mes tableaux numpy quand je ne connais pas le nombre d'éléments dans le tableau au début.