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 ?

0
jonnyd42 5 mars 2016 à 10:31

4 réponses

Meilleure réponse

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.

2
Ilja Everilä 7 mars 2016 à 09:10

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)
1
Kordi 5 mars 2016 à 09:36

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.

0
Marshall Farrier 5 mars 2016 à 09:05

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)
0
chthonicdaemon 7 mars 2016 à 15:28