over - python numpy to normal array




Der schnellste Weg, eine Liste von Indizes in ein 2D-Zahlenfeld von Einsen umzuwandeln (4)

Dies ist möglicherweise nicht der schnellste Weg. Sie müssen die Ausführungszeiten dieser Antworten mit großen Arrays vergleichen, um den schnellsten Weg zu finden. Hier ist meine Lösung

output = np.zeros((4,5))
for i, ix in enumerate(a):
    output[i][ix] = 1

# output -> 
#   array([[0, 1, 1, 0, 1],
#   [1, 0, 1, 1, 0],
#   [0, 1, 0, 1, 1],
#   [1, 0, 1, 0, 0]])

Ich habe eine Liste von Indizes

a = [
  [1,2,4],
  [0,2,3],
  [1,3,4],
  [0,2]]

Was ist der schnellste Weg, dies in ein numpy Array von Einsen umzuwandeln, wobei jeder Index die Position anzeigt, an der 1 auftreten würde?

Dh was ich will ist:

output = array([
  [0,1,1,0,1],
  [1,0,1,1,0],
  [0,1,0,1,1],
  [1,0,1,0,0]])

Ich kenne die maximale Größe des Arrays im Voraus. Ich weiß, ich könnte jede Liste durchlaufen und an jeder Indexposition eine 1 einfügen, aber gibt es eine schnellere / vektorisierte Möglichkeit, dies zu tun?

Mein Anwendungsfall könnte Tausende von Zeilen / Spalten enthalten, und ich muss dies Tausende Male tun. Je schneller desto besser.


Vielleicht nicht der beste Weg, aber der einzige Weg, den ich mir vorstellen kann:

output = np.zeros((4,5))
for i, (x, y) in enumerate(zip(a, output)):
    y[x] = 1
    output[i] = y
print(output)

Welche Ausgänge:

[[ 0.  1.  1.  0.  1.]
 [ 1.  0.  1.  1.  0.]
 [ 0.  1.  0.  1.  1.]
 [ 1.  0.  1.  0.  0.]]

Wie wäre es damit:

ncol = 5
nrow = len(a)
out = np.zeros((nrow, ncol), int)
out[np.arange(nrow).repeat([*map(len,a)]), np.concatenate(a)] = 1
out
# array([[0, 1, 1, 0, 1],
#        [1, 0, 1, 1, 0],
#        [0, 1, 0, 1, 1],
#        [1, 0, 1, 0, 0]])

Hier sind die Timings für ein 1000x1000-Binärarray. Beachten Sie, dass ich eine optimierte Version des oben genannten verwende (siehe Funktion pp unten):

pp 21.717635259992676 ms
ts 37.10938713003998 ms
u9 37.32933565042913 ms

Code zur Erstellung von Timings:

import itertools as it
import numpy as np

def make_data(n,m):
    I,J = np.where(np.random.random((n,m))<np.random.random((n,1)))
    return [*map(np.ndarray.tolist, np.split(J, I.searchsorted(np.arange(1,n))))]

def pp():
    sz = np.fromiter(map(len,a),int,nrow)
    out = np.zeros((nrow,ncol),int)
    out[np.arange(nrow).repeat(sz),np.fromiter(it.chain.from_iterable(a),int,sz.sum())] = 1
    return out

def ts():
    out = np.zeros((nrow,ncol),int)
    for i, ix in enumerate(a):
        out[i][ix] = 1
    return out

def u9():
    out = np.zeros((nrow,ncol),int)
    for i, (x, y) in enumerate(zip(a, out)):
        y[x] = 1
        out[i] = y
    return out

nrow,ncol = 1000,1000
a = make_data(nrow,ncol)

from timeit import timeit
assert (pp()==ts()).all()
assert (pp()==u9()).all()

print("pp", timeit(pp,number=100)*10, "ms")
print("ts", timeit(ts,number=100)*10, "ms")
print("u9", timeit(u9,number=100)*10, "ms")

Wie wäre es mit Array-Indizierung? Wenn Sie mehr über Ihre Eingabe wissen, müssen Sie möglicherweise nicht mehr in ein lineares Array konvertieren.

import numpy as np


def main():
    row_count = 4
    col_count = 5
    a = [[1,2,4],[0,2,3],[1,3,4],[0,2]]

    # iterate through each row, concatenate all indices and convert them to linear

    # numpy append performs copy even if you don't want it, list append is faster
    b = []
    for row_idx, row in enumerate(a):
        b.append(np.array(row, dtype=np.int64) + (row_idx * col_count))

    linear_idxs = np.hstack(b)
    #could skip previous steps if given index inputs well before hand, or in linear index order. 
    c = np.zeros(row_count * col_count)
    c[linear_idxs] = 1
    c = c.reshape(row_count, col_count)
    print(c)


if __name__ == "__main__":
    main()

#output
# [[0. 1. 1. 0. 1.]
#  [1. 0. 1. 1. 0.]
#  [0. 1. 0. 1. 1.]
#  [1. 0. 1. 0. 0.]]




numpy