J'ai environ 650 matrices basées sur CSV. Je prévois de charger chacun en utilisant Numpy comme dans l'exemple suivant:
m1 = numpy.loadtext(open("matrix1.txt", "rb"), delimiter=",", skiprows=1)
Il y a des fichiers matrix2.txt, matrix3.txt, ..., matrix650.txt que je dois traiter.
Mon objectif final est de multiplier chaque matrice les unes par les autres, ce qui signifie que je n'ai pas nécessairement à maintenir 650 matrices mais plutôt seulement 2 (1 en cours et 1 que je multiplie actuellement par en cours).
Voici un exemple de ce que je veux dire avec des matrices définies de 1 à n: M1, M2, M3, .., Mn.
M1 * M2 * M3 * ... * Mn
Les dimensions sur toutes les matrices sont les mêmes. Les matrices ne sont pas carrées. Il y a 197 lignes et 11 colonnes. Aucune des matrices n'est rare et chaque cellule entre en jeu.
Quelle est la meilleure façon / la plus efficace de le faire en python?
EDIT: J'ai pris ce qui a été suggéré et l'ai fait fonctionner en prenant la transposition car ce n'est pas une matrice carrée. En complément à la question, y a-t-il un moyen dans Numpy de faire une multiplication élément par élément ?
4 réponses
Une solution Python3, si "chaque matrice les unes par les autres" signifie en fait simplement les multiplier dans une rangée et les matrices ont des dimensions compatibles ((n, m) · (m, o) · (o, p ) · ...), que vous indiquez avec "(1 en cours et 1 que ...)", puis utilisez (si disponible):
from functools import partial
fnames = map("matrix{}.txt".format, range(1, 651))
np.linalg.multi_dot(map(partial(np.loadtxt, delimiter=',', skiprows=1), fnames))
Ou:
from functools import reduce, partial
fnames = map("matrix{}.txt".format, range(1, 651))
matrices = map(partial(np.loadtxt, delimiter=',', skiprows=1), fnames)
res = reduce(np.dot, matrices)
Les cartes, etc. sont paresseuses en python3, donc les fichiers sont lus au besoin. Loadtxt ne nécessite pas de fichier pré-ouvert, un nom de fichier fera l'affaire.
Faire toutes les combinaisons paresseusement, étant donné que les matrices ont la même forme (fera beaucoup de relecture des données):
from functools import partial
from itertools import starmap, combinations
map_loadtxt = partial(map, partial(np.loadtxt, delimiter=',', skiprows=1))
fname_combs = combinations(map("matrix{}.txt".format, range(1, 651)), 2)
res = list(starmap(np.dot, map(map_loadtxt, fname_combs)))
Utiliser un peu de regroupement pour réduire le rechargement des fichiers:
from itertools import groupby, combinations, chain
from functools import partial
from operator import itemgetter
loader = partial(np.loadtxt, delimiter=',', skiprows=1)
fname_pairs = combinations(map("matrix{}.txt".format, range(1, 651)), 2)
groups = groupby(fname_pairs, itemgetter(0))
res = list(chain.from_iterable(
map(loader(k).dot, map(loader, map(itemgetter(1), g)))
for k, g in groups
))
Étant donné que les matrices ne sont pas carrées, mais ont les mêmes dimensions, vous devez ajouter des transpositions avant la multiplication pour correspondre aux dimensions. Par exemple, loader(k).T.dot
ou map(np.transpose, map(loader, ...))
.
Si, en revanche, la question était réellement destinée à traiter la multiplication par élément, remplacez np.dot
par np.multiply
.
1. Variante: joli code mais lit toutes les matrices en même temps
matrixFileCount = 3
matrices = [np.loadtxt(open("matrix%s.txt" % i ), delimiter=",", skiprows=1) for i in range(1,matrixFileCount+1)]
allC = itertools.combinations([x for x in range(matrixFileCount)], 2)
allCMultiply = [np.dot(matrices[c[0]], matrices[c[1]]) for c in allC]
print allCMultiply
2. Variante: ne chargez que 2 fichiers à la fois, beau code mais beaucoup de rechargement
allCMulitply = []
fileList = ["matrix%s.txt" % x for x in range(1,matrixFileCount+1)]
allC = itertools.combinations(fileList, 2)
for c in allC:
m = [np.loadtxt(open(file), delimiter=",", skiprows=1) for file in c]
allCMulitply.append(np.dot(m[0], m[1]))
print allCMulitply
3. Variante: comme la seconde mais évitez de charger à chaque fois. Mais seulement 2 matrices à un moment donné dans la mémoire
Parce que les permutations créées avec itertools sont comme (1, 2), (1, 3), (1, 4), (2, 3), (2, 4), (3, 4)
, vous pouvez éviter parfois de charger les deux matrices.
matrixFileCount = 3
allCMulitply = []
mLoaded = {'file' : None, 'matrix' : None}
fileList = ["matrix%s.txt" % x for x in range(1,matrixFileCount+1)]
allC = itertools.combinations(fileList, 2)
for c in allC:
if c[0] is mLoaded['file']:
m = [mLoaded['matrix'], np.loadtxt(open(c[1]), delimiter=",", skiprows=1)]
else:
mLoaded = {'file' : None, 'matrix' : None}
m = [np.loadtxt(open(file), delimiter=",", skiprows=1) for file in c]
mLoaded = {'file' : c[0], 'matrix' : m[0]}
allCMulitply.append(np.dot(m[0], m[1]))
print allCMulitply
Performances
Si vous pouvez charger toutes les matrices en même temps dans la mémoire, la première partie est plus rapide que la seconde, car dans la seconde, vous rechargez beaucoup les matrices. La troisième partie est plus lente que la première, mais plus rapide que la seconde, car elle évite parfois de recharger les matrices.
0.943613052368 (Part 1: 10 Matrices a 2,2 with 1000 executions)
7.75622487068 (Part 2: 10 Matrices a 2,2 with 1000 executions)
4.83783197403 (Part 3: 10 Matrices a 2,2 with 1000 executions)
La réponse de Kordi charge toutes les matrices avant de faire la multiplication. Et c'est très bien si vous savez que les matrices vont être petites. Si vous souhaitez conserver la mémoire, cependant, je ferais ce qui suit:
import numpy as np
def get_dot_product(fnames):
assert len(fnames) > 0
accum_val = np.loadtxt(fnames[0], delimiter=',', skiprows=1)
return reduce(_product_from_file, fnames[1:], initializer=accum_val)
def _product_from_file(running_product, fname):
return running_product.dot(np.loadtxt(fname, delimiter=',', skiprows=1))
Si les matrices sont grandes et de forme irrégulière (non carrées), il existe également des algorithmes d'optimisation pour déterminer les groupements associatifs optimaux (c'est-à-dire où placer les parenthèses), mais dans la plupart des cas, je doute que cela en vaille la peine. décharger chaque fichier deux fois, une fois pour comprendre les regroupements associatifs, puis une fois pour le réaliser. NumPy est étonnamment rapide même sur de très grandes matrices.
Que diriez-vous d'une solution vraiment simple en évitant map
, reduce
et autres? L'objet tableau numpy
par défaut effectue une multiplication par élément par défaut.
size = (197, 11)
result = numpy.ones(size)
for i in range(1, 651):
result *= numpy.loadtext(open("matrix{}.txt".format(i), "rb"),
delimiter=",", skiprows=1)
Questions connexes
De nouvelles questions
python
Python est un langage de programmation multi-paradigme, typé dynamiquement et polyvalent. Il est conçu pour être rapide à apprendre, comprendre, utiliser et appliquer une syntaxe propre et uniforme. Veuillez noter que Python 2 est officiellement hors support à partir du 01-01-2020. Néanmoins, pour les questions Python spécifiques à la version, ajoutez la balise [python-2.7] ou [python-3.x]. Lorsque vous utilisez une variante Python (par exemple, Jython, PyPy) ou une bibliothèque (par exemple, Pandas et NumPy), veuillez l'inclure dans les balises.