Verketten Sie Numpy-Arrays ohne zu kopieren

74

In Numpy kann ich zwei Arrays Ende-zu-Ende mit np.appendoder verketten np.concatenate:

>>> X = np.array([[1,2,3]])
>>> Y = np.array([[-1,-2,-3],[4,5,6]])
>>> Z = np.append(X, Y, axis=0)
>>> Z
array([[ 1,  2,  3],
       [-1, -2, -3],
       [ 4,  5,  6]])

Aber diese machen Kopien ihrer Eingabearrays:

>>> Z[0,:] = 0
>>> Z
array([[ 0,  0,  0],
       [-1, -2, -3],
       [ 4,  5,  6]])
>>> X
array([[1, 2, 3]])

Gibt es eine Möglichkeit, zwei Arrays zu einer Ansicht zu verketten , dh ohne zu kopieren? Würde das eine np.ndarrayUnterklasse erfordern ?

Fred Foo
quelle
Warum möchten Sie lieber eine Ansicht als eine Kopie haben?
Winston Ewert
@WinstonEwert: Ich habe eine lange Liste von Arrays, für die ich eine einzelne globale Normalisierung durchführen möchte.
Fred Foo
Das Listenverständnis wird auch schnell sein.
Cyborg
Das beantwortet nicht die Frage, was ist falsch daran, all diese Arrays zu kopieren? Sind Sie grundsätzlich besorgt über die Kosten für das Kopieren oder möchten Sie die ursprünglichen Arrays ändern?
Winston Ewert
1
@ WinstonEwert: Die Kosten für das Kopieren sind das Problem; Andernfalls könnte ich nur concatenatesie und die ursprünglichen Arrays durch Ansichten in die Verkettung ersetzen. Sieht so aus, als müsste ich das tun.
Fred Foo

Antworten:

76

Der zu einem Numpy-Array gehörende Speicher muss zusammenhängend sein. Wenn Sie die Arrays separat zugewiesen haben, werden sie zufällig im Speicher verteilt, und es gibt keine Möglichkeit, sie als Ansichts-Numpy-Array darzustellen.

Wenn Sie vorher wissen, wie viele Arrays Sie benötigen, können Sie stattdessen mit einem großen Array beginnen, das Sie zuvor zugewiesen haben, und jedes der kleinen Arrays eine Ansicht des großen Arrays sein lassen (z. B. durch Schneiden erhalten).

pv.
quelle
12
Inkonsequente Bemerkung: Der Speicher einer Ansicht muss nicht zusammenhängend sein, sondern muss wahrscheinlich in festen Schritten angeordnet werden (was auch bei einer Liste von Arrays nicht der Fall ist).
Cyborg
Wollen Sie damit sagen, dass selbst eine Unterklasse nicht funktioniert? Ich weiß, dass Leute ndarrayUnterklassen verwenden, um mit mmap'd Arrays zu arbeiten, aber ich denke, Speicherzuordnungen sind auch zusammenhängend ...
Fred Foo
3
Ja, Unterklassen müssen auch dem Speichermodell von Numpy entsprechen. (Der Kommentar von @ cyborgs oben ist ebenfalls richtig: Die Sub-Arrays könnten auch mit festen Schritten im Speicher angeordnet werden, aber dies kann auch nur durch vorheriges Anordnen erreicht werden.) Das sorgfältige Lesen dieser Seite kann etwas mehr Licht ins Dunkel bringen.
pv.
Gibt es einen Grund, warum Sie das Schneiden vorschlagen, und nicht zB numpy.empty?
Lucidbrot
12

Initialisieren Sie einfach das Array, bevor Sie es mit Daten füllen. Wenn Sie möchten, können Sie mehr Speicherplatz als erforderlich zuweisen, und es wird aufgrund der Funktionsweise von numpy nicht mehr RAM belegt.

A = np.zeros(R,C)
A[row] = [data]

Der Speicher wird nur verwendet, wenn Daten in das Array eingegeben wurden. Das Erstellen eines neuen Arrays aus der Verkettung von zwei Arrays wird für ein Dataset beliebiger Größe, dh ein Dataset> 1 GB oder so, niemals abgeschlossen.

John
quelle
2

Überhaupt nicht wirklich elegant, aber Sie können mit einem Tupel zum Speichern von Zeigern auf die Arrays an das herangehen, was Sie möchten. Jetzt habe ich keine Ahnung, wie ich es in dem Fall verwenden würde, aber ich habe solche Dinge schon einmal gemacht.

>>> X = np.array([[1,2,3]])
>>> Y = np.array([[-1,-2,-3],[4,5,6]])
>>> z = (X, Y)
>>> z[0][:] = 0
>>> z
(array([[0, 0, 0]]), array([[-1, -2, -3],
       [ 4,  5,  6]]))
>>> X
array([[0, 0, 0]])
Brian Larsen
quelle
Ja, aber das gibt mir nicht die Art von NumPy-Indizierungsmagie, die ich gerne hätte. Trotzdem danke.
Fred Foo
1

Ich hatte das gleiche Problem und machte es umgekehrt. Nachdem ich normal (mit Kopie) verkettet hatte, ordnete ich die ursprünglichen Arrays neu zu, um Ansichten über das verkettete zu erhalten:

import numpy as np

def concat_no_copy(arrays):
    """ Concats the arrays and returns the concatenated array 
    in addition to the original arrays as views of the concatenated one.

    Parameters:
    -----------
    arrays: list
        the list of arrays to concatenate
    """
    con = np.concatenate(arrays)

    viewarrays = []
    for i, arr in enumerate(arrays):
        arrnew = con[sum(len(a) for a in arrays[:i]):
                     sum(len(a) for a in arrays[:i + 1])]
        viewarrays.append(arrnew)
        assert all(arr == arrnew)

    # return the view arrays, replace the old ones with these
    return con, viewarrays

Sie können es wie folgt testen:

def test_concat_no_copy():
    arr1 = np.array([0, 1, 2, 3, 4])
    arr2 = np.array([5, 6, 7, 8, 9])
    arr3 = np.array([10, 11, 12, 13, 14])

    arraylist = [arr1, arr2, arr3]

    con, newarraylist = concat_no_copy(arraylist)

    assert all(con == np.array([0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 
                                11, 12, 13, 14]))

    for old, new in zip(arraylist, newarraylist):
        assert all(old == new)
architektonisch
quelle
0

Sie können ein Array von Arrays erstellen, z.

>>> from numpy import *
>>> a = array([1.0, 2.0, 3.0])
>>> b = array([4.0, 5.0])
>>> c = array([a, b])
>>> c
array([[ 1.  2.  3.], [ 4.  5.]], dtype=object)
>>> a[0] = 100.0
>>> a
array([ 100.,    2.,    3.])
>>> c
array([[ 100.    2.    3.], [ 4.  5.]], dtype=object)
>>> c[0][1] = 200.0
>>> a
array([ 100.,  200.,    3.])
>>> c
array([[ 100.  200.    3.], [ 4.  5.]], dtype=object)
>>> c *= 1000
>>> c
array([[ 100000.  200000.    3000.], [ 4000.  5000.]], dtype=object)
>>> a
array([ 100.,  200.,    3.])
>>> # Oops! Copies were made...

Das Problem ist, dass bei Broadcast-Vorgängen Kopien erstellt werden (klingt wie ein Fehler).

e.tadeu
quelle
0

Die Antwort basiert auf meiner anderen Antwort in Bezug auf ndarray-Zeilen in ndarray

X = np.array([[1,2,3]])
Y = np.array([[-1,-2,-3],[4,5,6]])
Z = np.array([None, None, None])
Z[0] = X[0]
Z[1] = Y[0]
Z[2] = Y[1]

Z[0][0] = 5 # X would be changed as well

print(X)
Output: 
array([[5, 2, 3]])

# Let's make it a function!
def concat(X, Y, copy=True):
    """Return an array of references if copy=False""" 
    if copy is True:  # deep copy
        return np.append(X, Y, axis=0)
    len_x, len_y = len(X), len(Y)
    ret = np.array([None for _ in range(len_x + len_y)])
    for i in range(len_x):
        ret[i] = X[i]
    for j in range(len_y):
        ret[len_x + j] = Y[j] 
    return ret
Tai
quelle