J'ai expérimenté plusieurs façons de réduire numpy.ndarray. La dissociation se réfère ici à un traitement tel que le redimensionnement d'opencv. Cependant, puisque nous traitons de la dérégulation comme décrit ci-dessous, nous ne traitons pas des cas tels que l'agrandissement du tableau ou des cas où un côté de la dissociation ne peut pas diviser un côté du tableau d'origine.
Lorsque je mettais en œuvre un automate à gaz à réseau, il devenait nécessaire de dévier au stade du dessin des résultats. Plus précisément, je souhaite traiter le processus de calcul de la moyenne de 10000 * 10000 tableaux tous les 10 * 10 pour créer 1000 * 1000 tableaux. Il y a un redimensionnement dans opencv, et il devrait y avoir beaucoup de gens qui veulent l'utiliser, donc quand j'ai googlé qu'il n'y aurait rien (subjectif), un redimensionnement différent est sorti. Ensuite, c'est une bonne histoire d'utiliser le redimensionnement d'opencv, mais il est subtilement gênant qu'il ne prenne en charge que les entiers non signés de 1 octet, et si vous insérez un tableau d'autres types, une erreur sera générée. Comme il ne fait que dessiner le résultat, ce n'est pas si mal de l'utiliser, mais comme c'est une bonne idée, je veux utiliser une valeur précise, j'ai donc décidé de l'implémenter.
J'ai essayé les quatre types suivants.
--Préparez le tableau déparsé, retirez le tableau d'origine en tant que tranche et remplacez la valeur moyenne à l'aide de l'instruction for.
Dans la source suivante, le tableau original sera expliqué en tant que tableau (taille 10000 * 10000), et la longueur d'un côté à être éparse sera expliquée par mean_size (10 dans l'explication suivante).
Ci-dessous la source
mean_loop
def mean_loop(array, mean_size):
meaned_size = np.shape(array)[0] // mean_size
return_array = np.empty((meaned_size, meaned_size))
for y in range(meaned_size):
for x in range(meaned_size):
return_array[y, x] = array[mean_size*y:mean_size*(y+1), mean_size*x:mean_size*(x+1)].mean()
return return_array
C'est une mise en œuvre simple de ce que vous voulez faire. S'il est implémenté en C, ce serait comme ça. Cependant, cela semble désagréable lorsqu'il s'agit de doubles boucles en Python. De plus, cette fois, le tableau 10000 * 10000 sera 1000 * 1000, donc le temps d'exécution sera terrible.
mean_list_comprehension
def mean_list_comprehenion(array, mean_size):
meaned_size = np.shape(array)[0] // mean_size
return np.array([[array[mean_size*y:mean_size*(y+1), mean_size*x:mean_size*(x+1)].mean() for x in range(meaned_size)] for y in range(meaned_size)])
En parlant de Python, de la notation d'inclusion de liste et de la notation d'inclusion de liste, Python. On dit que ce sera plus rapide (subjectif), donc vous pouvez vous y attendre. Cependant, la boucle est également de 1000 * 1000 ici. Cependant, comme je l'ai écrit, la notation d'inclusion de double liste est merdique et difficile à lire.
mean_slice
def mean_slice(array, mean_size):
sum_row_array = np.array(array[::mean_size])
for row in range(1, mean_size):
sum_row_array += array[row::mean_size]
sum_column_array = sum_row_array[:,::mean_size]
for column in range(1, mean_size):
sum_column_array += sum_row_array[:,column::mean_size]
return sum_column_array / mean_size**2
Une méthode un peu boueuse. La boucle n'est que de 20 fois pour ajouter 10 lignes et 10 colonnes. Il devrait être plus rapide que les deux ci-dessus par tous les moyens.
mean_np_roll
def mean_np_roll(array, mean_size):
sum_row_array = np.array(array)
for row in range(1, mean_size):
sum_row_array += np.roll(array, -row, axis=0)
sum_column_array = np.array(sum_row_array)
for column in range(1, mean_size):
sum_column_array += np.roll(sum_row_array, -column, axis=1)
return sum_column_array[::mean_size, ::mean_size] / mean_size ** 2
Ce qu'ils font est légèrement différent, mais l'idée est la même. J'ai entendu dire que ndarray est lent à accéder aux éléments, alors j'ai pensé que cette tranche pourrait être lente, alors je l'ai fait. Cependant, alors que la méthode des tranches réduit la zone à ajouter, elle semble être plus lente à cet égard car elle reste ici la même taille que le tableau d'origine.
Je voulais le voir dans divers contextes expérimentaux, j'ai donc défini la taille moyenne d'un côté de la séquence originale comme (3000, 10), (6000, 10), (9000, 10), (3000, 100), (6000, 100). Je l'ai changé en, (3000, 1000) et je l'ai calculé. Voici le résultat de l'exécution.
Comme prévu, slice est plus rapide que les deux ci-dessus, mais la méthode utilisant np.roll était plus lente que prévu. Surtout quand la surface moyenne est grande, la lenteur est assez incroyable. En outre, le résultat est que la notation d'inclusion de liste n'est pas si rapide. Compte tenu de ces résultats, je pense que ce serait une mauvaise idée de forcer la notation du point de vue de la lisibilité. Pour le moment, la fonction ci-dessus utilisant slice semble être bonne, donc je pense qu'il vaut mieux l'utiliser.
J'ai essayé d'implémenter la méthode supérieure en C. L'extension est cpp, mais le contenu est ordinaire C. J'entends que c'est rapide pour une raison quelconque, alors j'utilise du po brut. Les détails sont différents, comme la partie où le tableau unidimensionnel est considéré comme secondaire, mais quand il s'agit de l'implémentation en C, cela devrait être un tel code, donc je n'ai pas pensé à l'équité dans cette mesure. J'ai créé une version x64 dans la communauté Visual Studio 2017.
mean_loop.cpp
#include "stdafx.h"
#include <stdio.h>
#include <time.h>
#include <Windows.h>
double* mean_loop(int mean_size, int array_size, double* array) {
double *return_array;
int meaned_size = array_size / mean_size;
return_array = new double[meaned_size * meaned_size];
for (size_t i = 0; i < meaned_size; i++)
{
for (size_t j = 0; j < meaned_size; j++)
{
double temp = 0;
for (size_t k = 0; k < mean_size; k++)
{
for (size_t l = 0; l < mean_size; l++)
{
temp += array[(i + k) * meaned_size * mean_size + j*mean_size + l];
}
}
return_array[i * meaned_size+ j] = temp / (mean_size * mean_size);
}
}
return return_array;
};
int main()
{
double *array, *return_array;
int mean_size = 10, array_size = 3000;
clock_t start, end, span;
int array_sizes[] = {3000, 6000, 9000};
int mean_sizes[] = { 10, 100, 1000 };
for (size_t as = 0; as < 3; as++)
{
for (size_t ms = 0; ms < 3; ms++)
{
array_size = array_sizes[as];
mean_size = mean_sizes[ms];
array = new double[array_size * array_size];
for (size_t i = 0; i < array_size * array_size; i++)
{
array[i] = i;
}
start = clock();
return_array = mean_loop(mean_size, array_size, array);
end = clock();
span = end - start;
printf("%d %4d took: %lf\n", array_size, mean_size, (double)span / CLOCKS_PER_SEC);
delete return_array, array;
}
}
system("pause");
return 0;
}
Le résultat de ce calcul. Après tout, c'est rapide! J'ai peur des fuites de mémoire parce que c'est brut, mais c'est un effort (théorie extrême), et comme les calculs à grande échelle sont souvent effectués dans des calculs numériques, C / C ++ doit toujours être utilisé. Cependant, en échange d'une telle horreur, je pense que cette vitesse est au bon endroit lorsque l'on compare le code de Python relativement sûr et raisonnablement rapide ci-dessus.
N'utilisez pas de langage de script pour les calculs numériques.
Recommended Posts