[Python3] Geschwindigkeitsvergleich usw. über den Entzug von numpy.ndarray

Abstrakt

Ich habe mit verschiedenen Methoden experimentiert, um numpy.ndarray zu schonen. Die Dissoziation bezieht sich hier auf die Verarbeitung wie die Größenänderung von opencv. Da es sich jedoch um die nachstehend beschriebene Deregulierung handelt, handelt es sich nicht um Fälle wie die Vergrößerung des Arrays oder Fälle, in denen eine Seite der Dissoziation eine Seite des ursprünglichen Arrays nicht teilen kann.

Vorwort

Als ich einen Gittergasautomaten implementierte, wurde es notwendig, beim Zeichnen der Ergebnisse abzuweichen. Insbesondere möchte ich den Prozess der Mittelung von 10000 * 10000 Arrays alle 10 * 10 verarbeiten, um 1000 * 1000 Arrays zu erstellen. Es gibt eine Größenänderung in opencv, und es sollte viele Leute geben, die sie verwenden möchten. Als ich also googelte, dass es nichts (subjektives) geben würde, kam eine andere Größenänderung heraus. Dann ist es eine gute Geschichte, die Größenänderung von opencv zu verwenden, aber es ist auf subtile Weise problematisch, dass nur vorzeichenlose 1-Byte-Ganzzahlen unterstützt werden. Wenn Sie ein Array anderer Typen einfügen, wird ein Fehler ausgegeben. Da nur das Ergebnis gezeichnet wird, ist es nicht so schlecht, dies zu verwenden. Da es jedoch eine gute Idee ist, möchte ich einen genauen Wert verwenden, und habe mich daher entschlossen, ihn zu implementieren.

Ausführungsumgebung

Methode ausprobiert

Ich habe die folgenden vier Typen ausprobiert.

In der folgenden Quelle wird das ursprüngliche Array als Array (Größe 10000 * 10000) und die Länge einer Seite mit geringer Dichte als mean_size (10 in der folgenden Erklärung) erläutert.

Bereiten Sie das abgegrenzte Array vor, nehmen Sie das ursprüngliche Array als Slice heraus und ersetzen Sie den gemittelten Wert durch die for-Anweisung.

Unten ist die Quelle

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

Es ist eine einfache Implementierung dessen, was Sie tun möchten. Wenn es in C implementiert ist, wäre es so. Es fühlt sich jedoch unangenehm an, wenn es um Doppelschleifen in Python geht. Außerdem wird dieses Mal das 10000 * 10000-Array 1000 * 1000 sein, so dass die Ausführungszeit schrecklich sein wird.

Beschreiben Sie das obige Verfahren in der Listeneinschlussnotation

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)])

Apropos Python, Listeneinschlussnotation, und Apropos Listeneinschlussnotation, Python. Es wird gesagt, dass es schneller (subjektiv) sein wird, also können Sie es erwarten. Die Schleife ist hier jedoch auch 1000 * 1000. Wie ich es geschrieben habe, ist die Doppellisten-Einschlussnotation jedoch beschissen und schwer zu lesen.

Schneiden Sie das ursprüngliche Array in Scheiben und fügen Sie es in Zeilen und Spalten hinzu

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

Eine etwas schlammige Methode. Die Schleife ist nur 20 Mal, um 10 Zeilen und 10 Spalten hinzuzufügen. Es sollte auf jeden Fall schneller sein als die beiden oben genannten.

Beschreiben Sie das obige Verfahren mit np.roll

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

Was sie tun, ist etwas anders, aber die Idee ist dieselbe. Ich habe gehört, dass ndarray nur langsam auf Elemente zugreift, also dachte ich, dass Slice langsam sein könnte, also habe ich es geschafft. Während die Slice-Methode den hinzuzufügenden Bereich verkleinert, scheint sie in dieser Hinsicht langsamer zu sein, da sie hier dieselbe Größe wie das ursprüngliche Array hat.

Ergebnis

Ich wollte es in verschiedenen experimentellen Einstellungen sehen, also stellte ich die durchschnittliche Größe einer Seite der ursprünglichen Sequenz als (3000, 10), (6000, 10), (9000, 10), (3000, 100), (6000, 100) ein. Ich habe es in (3000, 1000) geändert und berechnet. Das Folgende ist das Ausführungsergebnis.

image.png

Wie erwartet ist Slice schneller als die beiden oben genannten, aber die Methode mit np.roll war langsamer als erwartet. Besonders wenn die durchschnittliche Fläche groß ist, ist die Langsamkeit ziemlich erstaunlich. Das Ergebnis ist auch, dass die Notation der Listeneinbeziehung nicht so schnell ist. Angesichts dieser Ergebnisse halte ich es für eine schlechte Idee, die Einschlussnotation unter dem Gesichtspunkt der Lesbarkeit zu erzwingen. Derzeit scheint die obige Funktion mit Slice gut zu sein, daher denke ich, dass es besser ist, diese zu verwenden.

(Bonus?) Vergleich mit C.

Ich habe versucht, die Top-Methode in C zu implementieren. Die Erweiterung ist cpp, aber der Inhalt ist normal C. Ich höre, dass es aus irgendeinem Grund schnell ist, also benutze ich rohen Po. Die Details sind unterschiedlich, wie zum Beispiel der Teil, in dem das eindimensionale Array als sekundär angesehen wird, aber wenn es um die Implementierung in C geht, sollte es ein solcher Code sein, also habe ich nicht in diesem Ausmaß an Fairness gedacht. Ich habe ein x64-Release in der Visual Studio 2017-Community erstellt.

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;
}

Das Ergebnis dieser Berechnung. loop_c++.PNG Immerhin ist es schnell! Ich habe Angst vor Speicherlecks, weil es ein roher Po ist, aber das ist eine Anstrengung (extreme Theorie), und da umfangreiche Berechnungen häufig in numerischen Berechnungen durchgeführt werden, sollte C / C ++ weiterhin verwendet werden. Als Gegenleistung für diesen Horror bin ich jedoch der Meinung, dass diese Geschwindigkeit beim Vergleich des oben genannten relativ sicheren und relativ schnellen Codes von Python am richtigen Ort ist.

Zusammenfassung

Verwenden Sie keine Skriptsprache für numerische Berechnungen.

Recommended Posts

[Python3] Geschwindigkeitsvergleich usw. über den Entzug von numpy.ndarray
Geschwindigkeitsvergleich der Python-XML-Perspektive
Geschwindigkeitsvergleich von Python, Java, C ++
Vergleich von 4 Arten von Python-Webframeworks
Beispiele wie Python Getter und Setter
Geschwindigkeitsvergleich der Volltextverarbeitung von Wiktionary mit F # und Python
Vergleich der Berechnungsgeschwindigkeit durch Implementierung von Python mpmath (willkürliche Genauigkeitsberechnung) (Hinweis)
Vergleich japanischer Konvertierungsmodule in Python3
Python-String-Vergleich / benutze 'Liste' und 'In' anstelle von '==' und 'oder'
Geschwindigkeitsvergleich des Teilens in Python / Janome, Sudachi, Ginza, Mecab, Fugashi, Tinysegmenter
Versuchen Sie den Geschwindigkeitsvergleich der BigQuery Storage API
Vergleich von Python Serverless Frameworks-Zappa mit Chalice
Vergleich der Matrixtranspositionsgeschwindigkeit durch Python
Geschwindigkeitsvergleich von murmurhash3, md5 und sha1
Python Geschwindigkeitsvergleich Regex vs Startwith vs Str [: word_length]
Unbeaufsichtigter Betrieb von Google Spreadsheets (usw.) in Python
Vergleichen Sie die Geschwindigkeit von Python Append und Map
Gründlicher Vergleich von drei morphologischen Python-Analysebibliotheken
Einfacher Vergleich von Python-Bibliotheken, die Excel betreiben
Geschwindigkeit: Element am Ende des Python-Arrays hinzufügen
R- und Python-Schreibvergleich (euklidische Methode der gegenseitigen Teilung)
Geschwindigkeitsbewertung der Ausgabe von CSV-Dateien in Python
Vergleich von Python und Ruby (Environment / Grammar / Literal Edition)
Verwendung für Python-Stapel und -Warteschlangen (Geschwindigkeitsvergleich jeder Datenstruktur)
Erster Python 3 ~ Erster Vergleich ~
Python-Grundlagen ①
Grundlagen von Python ①
Kopie von Python
Einführung von Python
Geschwindigkeitsvergleich jeder Sprache nach der Monte-Carlo-Methode
Python netCDF4 Lesegeschwindigkeit und Verschachtelung von for-Anweisungen
Experiment zum Vergleich der Schreibgeschwindigkeit von Dateien zwischen Python 2.7.9 und Pypy 2.5.0
Vergleich der Implementierung mehrerer exponentieller gleitender Durchschnitte (DEMA, TEMA) in Python
Ein schneller Vergleich der Testbibliotheken von Python und node.js.
Vergleichstabelle häufig verwendeter Prozesse von Python und Clojure
Vergleich von CoffeeScript mit JavaScript-, Python- und Ruby-Grammatik