"Kloning" vektor baris atau kolom

155

Terkadang berguna untuk "mengkloning" vektor baris atau kolom ke sebuah matriks. Dengan kloning maksud saya mengkonversi vektor baris seperti

[1,2,3]

Ke dalam sebuah matriks

[[1,2,3]
 [1,2,3]
 [1,2,3]
]

atau vektor kolom seperti

[1
 2
 3
]

ke

[[1,1,1]
 [2,2,2]
 [3,3,3]
]

Di matlab atau oktaf ini dilakukan dengan cukup mudah:

 x = [1,2,3]
 a = ones(3,1) * x
 a =

    1   2   3
    1   2   3
    1   2   3

 b = (x') * ones(1,3)
 b =

    1   1   1
    2   2   2
    3   3   3

Saya ingin mengulangi ini dalam numpy, tetapi tidak berhasil

In [14]: x = array([1,2,3])
In [14]: ones((3,1)) * x
Out[14]:
array([[ 1.,  2.,  3.],
       [ 1.,  2.,  3.],
       [ 1.,  2.,  3.]])
# so far so good
In [16]: x.transpose() * ones((1,3))
Out[16]: array([[ 1.,  2.,  3.]])
# DAMN
# I end up with 
In [17]: (ones((3,1)) * x).transpose()
Out[17]:
array([[ 1.,  1.,  1.],
       [ 2.,  2.,  2.],
       [ 3.,  3.,  3.]])

Mengapa metode pertama ( In [16]) tidak berfungsi? Apakah ada cara untuk mencapai tugas ini dengan python dengan cara yang lebih elegan?

Boris Gorelik
sumber
6
Dalam Matlab, perhatikan bahwa ini jauh lebih cepat untuk digunakan repmat: repmat([1 2 3],3,1)ataurepmat([1 2 3].',1,3)
Luis Mendo
Oktaf juga punya repmat.
ma11hew28
Bagi mereka yang ingin melakukan hal yang sama dengan pframe dataframe checkout yang tile_df tertaut di sini
zelusp

Jawaban:

80

Berikut cara Pythonic yang elegan untuk melakukannya:

>>> array([[1,2,3],]*3)
array([[1, 2, 3],
       [1, 2, 3],
       [1, 2, 3]])

>>> array([[1,2,3],]*3).transpose()
array([[1, 1, 1],
       [2, 2, 2],
       [3, 3, 3]])

masalah dengan [16]tampaknya bahwa transpose tidak berpengaruh pada array. Anda mungkin menginginkan sebuah matriks sebagai gantinya:

>>> x = array([1,2,3])
>>> x
array([1, 2, 3])
>>> x.transpose()
array([1, 2, 3])
>>> matrix([1,2,3])
matrix([[1, 2, 3]])
>>> matrix([1,2,3]).transpose()
matrix([[1],
        [2],
        [3]])
Peter
sumber
1
(Transposisi berfungsi untuk array 2D, misalnya untuk yang berbentuk bujur sangkar pada contoh, atau ketika berubah menjadi (N,1)array -shape using .reshape(-1, 1))
Mark
34
Ini sangat tidak efisien. Gunakan numpy.tileseperti yang ditunjukkan pada jawaban pv .
David Heffernan
304

Menggunakan numpy.tile :

>>> tile(array([1,2,3]), (3, 1))
array([[1, 2, 3],
       [1, 2, 3],
       [1, 2, 3]])

atau untuk kolom berulang:

>>> tile(array([[1,2,3]]).transpose(), (1, 3))
array([[1, 1, 1],
       [2, 2, 2],
       [3, 3, 3]])
pv.
sumber
16
Suara positif! Pada sistem saya, untuk vektor dengan 10.000 elemen diulang 1000 kali, tilemetode ini 19,5 kali lebih cepat daripada metode dalam jawaban yang saat ini diterima (menggunakan metode multiplication-operator-method).
Dr. Jan-Philip Gehrcke
1
Di bagian kedua ("kolom berulang"), dapatkah Anda menjelaskan apa yang dilakukan oleh kurung siku kedua, yaitu [[1,2,3]]
Ant
@Apakah itu menjadi array 2D dengan panjang 1 pada sumbu pertama (vertikal pada layar Anda) dan panjang 3 pada sumbu kedua (horizontal pada layar Anda). Transposing kemudian membuatnya memiliki panjang 3 di sumbu pertama dan panjang 1 di sumbu kedua. Bentuk ubin (1, 3)menyalin kolom ini lebih dari tiga kali, itulah sebabnya mengapa setiap baris hasil mengandung elemen tunggal yang berbeda.
BallpointBen
Ini harus menjadi jawaban yang diterima karena Anda dapat melewatkan vektor apa pun yang sudah diinisialisasi sementara yang diterima hanya dapat berfungsi jika Anda menambahkan koma saat Anda menginisialisasi vektor. Terima kasih!
Yohan Obadia
Saya tidak bisa mendapatkan ini bekerja untuk solusi 2d ke 3d :(
john ktejik
42

Pertama-tama perhatikan bahwa dengan operasi penyiaran numpy , biasanya tidak perlu menduplikasi baris dan kolom. Lihat ini dan ini untuk deskripsi.

Tetapi untuk melakukan ini, repeat dan newaxis mungkin adalah cara terbaik

In [12]: x = array([1,2,3])

In [13]: repeat(x[:,newaxis], 3, 1)
Out[13]: 
array([[1, 1, 1],
       [2, 2, 2],
       [3, 3, 3]])

In [14]: repeat(x[newaxis,:], 3, 0)
Out[14]: 
array([[1, 2, 3],
       [1, 2, 3],
       [1, 2, 3]])

Contoh ini untuk vektor baris, tetapi menerapkan ini ke vektor kolom mudah-mudahan jelas. pengulangan sepertinya mengeja ini dengan baik, tetapi Anda juga bisa melakukannya melalui perkalian seperti dalam contoh Anda

In [15]: x = array([[1, 2, 3]])  # note the double brackets

In [16]: (ones((3,1))*x).transpose()
Out[16]: 
array([[ 1.,  1.,  1.],
       [ 2.,  2.,  2.],
       [ 3.,  3.,  3.]])
Tom10
sumber
5
Newaxis memiliki manfaat tambahan yaitu tidak benar-benar menyalin data sampai diperlukan. Jadi jika Anda melakukan ini untuk melipatgandakan atau menambah array 3x3 lainnya, ulangi tidak perlu. Baca di siaran numpy untuk mendapatkan ide.
AFoglia
@AFoglia - Poin bagus. Saya memperbarui jawaban saya untuk menunjukkan ini.
Tom10
1
Apa manfaat menggunakan np.repeatvs np.tile?
mrgloom
@ MrGloom: Tidak ada, sebagian besar, untuk kasus ini. Untuk array 1D kecil, mereka serupa dan tidak ada perbedaan yang signifikan / manfaat / keuntungan / dll. Secara pribadi, saya menemukan simetri antara baris dan kloning kolom menjadi lebih intuitif, dan saya tidak suka transpose yang dibutuhkan untuk ubin, tetapi itu hanya masalah selera. Jawaban Mateen Ulhaq juga mengatakan pengulangan lebih cepat, tetapi ini mungkin tergantung pada kasus penggunaan tepat yang dipertimbangkan, meskipun pengulangan jauh lebih dekat dengan fungsi-C, jadi kemungkinan akan tetap agak lebih cepat. Dalam 2D ​​mereka memiliki perilaku yang berbeda sehingga itu penting di sana.
tom10
12

Membiarkan:

>>> n = 1000
>>> x = np.arange(n)
>>> reps = 10000

Alokasi tanpa biaya

Sebuah pandangan tidak mengambil memori tambahan. Dengan demikian, deklarasi ini bersifat instan:

# New axis
x[np.newaxis, ...]

# Broadcast to specific shape
np.broadcast_to(x, (reps, n))

Alokasi paksa

Jika Anda ingin memaksa konten berada di memori:

>>> %timeit np.array(np.broadcast_to(x, (reps, n)))
10.2 ms ± 62.3 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)

>>> %timeit np.repeat(x[np.newaxis, :], reps, axis=0)
9.88 ms ± 52.4 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)

>>> %timeit np.tile(x, (reps, 1))
9.97 ms ± 77.3 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)

Ketiga metode ini memiliki kecepatan yang kira-kira sama.

Komputasi

>>> a = np.arange(reps * n).reshape(reps, n)
>>> x_tiled = np.tile(x, (reps, 1))

>>> %timeit np.broadcast_to(x, (reps, n)) * a
17.1 ms ± 284 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)

>>> %timeit x[np.newaxis, :] * a
17.5 ms ± 300 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)

>>> %timeit x_tiled * a
17.6 ms ± 240 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)

Ketiga metode ini memiliki kecepatan yang kira-kira sama.


Kesimpulan

Jika Anda ingin mereplikasi sebelum perhitungan, pertimbangkan untuk menggunakan salah satu metode "alokasi biaya nol". Anda tidak akan menderita penalti kinerja "alokasi paksa".

Mateen Ulhaq
sumber
8

Saya pikir menggunakan siaran dalam numpy adalah yang terbaik, dan lebih cepat

Saya melakukan perbandingan sebagai berikut

import numpy as np
b = np.random.randn(1000)
In [105]: %timeit c = np.tile(b[:, newaxis], (1,100))
1000 loops, best of 3: 354 µs per loop

In [106]: %timeit c = np.repeat(b[:, newaxis], 100, axis=1)
1000 loops, best of 3: 347 µs per loop

In [107]: %timeit c = np.array([b,]*100).transpose()
100 loops, best of 3: 5.56 ms per loop

sekitar 15 kali lebih cepat menggunakan siaran

smartkevin
sumber
Anda dapat mengindeks dengan Noneuntuk melakukan hal yang sama.
DanielSank
apa yang baru?
dreab
np.newaxis adalah alias untuk Tidak Ada
john ktejik
ulangi lebih cepat: 5,56 ms = 5560 µs
Augusto Fadel
4

Salah satu solusi bersih adalah menggunakan fungsi produk luar NumPy dengan vektornya:

np.outer(np.ones(n), x)

memberikan nbaris berulang. Ganti urutan argumen untuk mendapatkan kolom berulang. Untuk mendapatkan jumlah baris dan kolom yang sama dengan yang mungkin Anda lakukan

np.outer(np.ones_like(x), x)
Jon Deaton
sumber
3

Kamu bisa memakai

np.tile(x,3).reshape((4,3))

ubin akan menghasilkan repetisi dari vektor

dan membentuk kembali akan memberikan bentuk yang Anda inginkan

penghitung uang
sumber
1

Jika Anda memiliki kerangka data panda dan ingin mempertahankan dtypes, bahkan kategorikal, ini adalah cara cepat untuk melakukannya:

import numpy as np
import pandas as pd
df = pd.DataFrame({1: [1, 2, 3], 2: [4, 5, 6]})
number_repeats = 50
new_df = df.reindex(np.tile(df.index, number_repeats))
Kucing Unfun
sumber
-1
import numpy as np
x=np.array([1,2,3])
y=np.multiply(np.ones((len(x),len(x))),x).T
print(y)

hasil:

[[ 1.  1.  1.]
 [ 2.  2.  2.]
 [ 3.  3.  3.]]
kibitzforu
sumber