Perbedaan antara bentuk numpy.array (R, 1) dan (R,)

320

Dalam numpy, beberapa operasi kembali dalam bentuk (R, 1)tetapi beberapa kembali (R,). Ini akan membuat perkalian matriks lebih membosankan karena reshapediperlukan eksplisit . Sebagai contoh, diberi matriks M, jika kita ingin melakukan di numpy.dot(M[:,0], numpy.ones((1, R)))mana Rjumlah baris (tentu saja, masalah yang sama juga terjadi pada kolom-bijaksana). Kami akan mendapatkan matrices are not alignedkesalahan karena M[:,0]dalam bentuk (R,)tetapi numpy.ones((1, R))dalam bentuk (1, R).

Jadi pertanyaan saya adalah:

  1. Apa perbedaan antara bentuk (R, 1)dan (R,). Saya tahu daftar nomor dan daftar di mana semua daftar hanya berisi angka. Hanya ingin tahu mengapa tidak mendesain numpysehingga lebih menyukai bentuk (R, 1)daripada (R,)untuk perkalian matriks yang lebih mudah.

  2. Adakah cara yang lebih baik untuk contoh di atas? Tanpa secara eksplisit membentuk kembali seperti ini:numpy.dot(M[:,0].reshape(R, 1), numpy.ones((1, R)))

Clwen
sumber
3
Ini mungkin bisa membantu. Namun tidak dengan menemukan solusi praktis.
keyser
1
Solusi yang tepat: numpy.ravel (M [:, 0]) - mengubah bentuk dari (R, 1) menjadi (R,)
Andi R

Jawaban:

545

1. Arti bentuk dalam NumPy

Anda menulis, "Saya tahu ini daftar angka dan daftar daftar di mana semua daftar hanya berisi angka" tetapi itu sedikit cara yang tidak membantu untuk memikirkannya.

Cara terbaik untuk berpikir tentang array NumPy adalah bahwa mereka terdiri dari dua bagian, buffer data yang hanya merupakan blok elemen mentah, dan pandangan yang menjelaskan cara menafsirkan buffer data.

Misalnya, jika kita membuat array 12 bilangan bulat:

>>> a = numpy.arange(12)
>>> a
array([ 0,  1,  2,  3,  4,  5,  6,  7,  8,  9, 10, 11])

Kemudian aterdiri dari buffer data, disusun seperti ini:

┌────┬────┬────┬────┬────┬────┬────┬────┬────┬────┬────┬────┐
  0   1   2   3   4   5   6   7   8   9  10  11 
└────┴────┴────┴────┴────┴────┴────┴────┴────┴────┴────┴────┘

dan tampilan yang menjelaskan cara menafsirkan data:

>>> a.flags
  C_CONTIGUOUS : True
  F_CONTIGUOUS : True
  OWNDATA : True
  WRITEABLE : True
  ALIGNED : True
  UPDATEIFCOPY : False
>>> a.dtype
dtype('int64')
>>> a.itemsize
8
>>> a.strides
(8,)
>>> a.shape
(12,)

Di sini bentuknya (12,) berarti array diindeks oleh indeks tunggal yang berjalan dari 0 hingga 11. Secara konseptual, jika kita memberi label pada indeks tunggal ini i, array tersebut aterlihat seperti ini:

i= 0    1    2    3    4    5    6    7    8    9   10   11
┌────┬────┬────┬────┬────┬────┬────┬────┬────┬────┬────┬────┐
  0   1   2   3   4   5   6   7   8   9  10  11 
└────┴────┴────┴────┴────┴────┴────┴────┴────┴────┴────┴────┘

Jika kami membentuk kembali sebuah array, ini tidak mengubah buffer data. Alih-alih, itu menciptakan tampilan baru yang menjelaskan cara berbeda untuk menginterpretasikan data. Jadi setelah:

>>> b = a.reshape((3, 4))

array bmemiliki buffer data yang sama dengan a, tetapi sekarang diindeks oleh dua indeks yang berjalan dari 0 ke 2 dan 0 hingga 3 masing-masing. Jika kami memberi label pada dua indeks idan j, array bterlihat seperti ini:

i= 0    0    0    0    1    1    1    1    2    2    2    2
j= 0    1    2    3    0    1    2    3    0    1    2    3
┌────┬────┬────┬────┬────┬────┬────┬────┬────┬────┬────┬────┐
  0   1   2   3   4   5   6   7   8   9  10  11 
└────┴────┴────┴────┴────┴────┴────┴────┴────┴────┴────┴────┘

yang berarti:

>>> b[2,1]
9

Anda dapat melihat bahwa indeks kedua berubah dengan cepat dan indeks pertama berubah dengan lambat. Jika Anda memilih ini sebagai sebaliknya, Anda dapat menentukan orderparameter:

>>> c = a.reshape((3, 4), order='F')

yang menghasilkan array yang diindeks seperti ini:

i= 0    1    2    0    1    2    0    1    2    0    1    2
j= 0    0    0    1    1    1    2    2    2    3    3    3
┌────┬────┬────┬────┬────┬────┬────┬────┬────┬────┬────┬────┐
  0   1   2   3   4   5   6   7   8   9  10  11 
└────┴────┴────┴────┴────┴────┴────┴────┴────┴────┴────┴────┘

yang berarti:

>>> c[2,1]
5

Seharusnya sekarang menjadi jelas apa artinya bagi array untuk memiliki bentuk dengan satu atau lebih dimensi ukuran 1. Setelah:

>>> d = a.reshape((12, 1))

array ddiindeks oleh dua indeks, yang pertama berjalan dari 0 hingga 11, dan indeks kedua selalu 0:

i= 0    1    2    3    4    5    6    7    8    9   10   11
j= 0    0    0    0    0    0    0    0    0    0    0    0
┌────┬────┬────┬────┬────┬────┬────┬────┬────┬────┬────┬────┐
  0   1   2   3   4   5   6   7   8   9  10  11 
└────┴────┴────┴────┴────┴────┴────┴────┴────┴────┴────┴────┘

sehingga:

>>> d[10,0]
10

Dimensi panjang 1 adalah "bebas" (dalam arti tertentu), jadi tidak ada yang menghentikan Anda untuk pergi ke kota:

>>> e = a.reshape((1, 2, 1, 6, 1))

memberikan array yang diindeks seperti ini:

i= 0    0    0    0    0    0    0    0    0    0    0    0
j= 0    0    0    0    0    0    1    1    1    1    1    1
k= 0    0    0    0    0    0    0    0    0    0    0    0
l= 0    1    2    3    4    5    0    1    2    3    4    5
m= 0    0    0    0    0    0    0    0    0    0    0    0
┌────┬────┬────┬────┬────┬────┬────┬────┬────┬────┬────┬────┐
  0   1   2   3   4   5   6   7   8   9  10  11 
└────┴────┴────┴────┴────┴────┴────┴────┴────┴────┴────┴────┘

sehingga:

>>> e[0,1,0,0,0]
6

Lihat dokumentasi internal NumPy untuk detail lebih lanjut tentang bagaimana array diterapkan.

2. Apa yang harus dilakukan?

Karena numpy.reshapehanya menciptakan tampilan baru, Anda tidak perlu takut menggunakannya kapan pun diperlukan. Ini alat yang tepat untuk digunakan ketika Anda ingin mengindeks array dengan cara yang berbeda.

Namun, dalam perhitungan yang panjang biasanya memungkinkan untuk mengatur untuk membangun array dengan bentuk "benar", dan dengan demikian meminimalkan jumlah pembentukan kembali dan transpos. Tetapi tanpa melihat konteks aktual yang mengarah pada kebutuhan untuk membentuk kembali, sulit untuk mengatakan apa yang harus diubah.

Contoh dalam pertanyaan Anda adalah:

numpy.dot(M[:,0], numpy.ones((1, R)))

tetapi ini tidak realistis. Pertama, ungkapan ini:

M[:,0].sum()

menghitung hasilnya lebih sederhana. Kedua, apakah benar-benar ada sesuatu yang istimewa tentang kolom 0? Mungkin yang sebenarnya Anda butuhkan adalah:

M.sum(axis=0)
Gareth Rees
sumber
34
Ini sangat membantu dalam memikirkan bagaimana array disimpan. Terima kasih! Mengakses kolom (atau baris) dari matriks (2-d) untuk perhitungan matriks lebih lanjut tidak nyaman meskipun karena saya selalu harus membentuk kembali kolom dengan tepat. Setiap kali saya harus mengubah bentuk dari (n,) ke (n, 1).
OfLettersAndNumbers
3
@ SammyLee: Gunakan newaxisjika Anda memerlukan sumbu lain, misalnya a[:, j, np.newaxis]adalah jkolom th a, dan a[np.newaxis, i]adalah ibaris th.
Gareth Rees
saya mencoba untuk merencanakan indeks untuk mendapatkan pemahaman yang lebih baik di atas kertas oleh model ini dan saya sepertinya tidak mengerti, jika saya memiliki bentuk 2 x 2 x 4 saya mengerti 2 pertama dapat dipahami sebagai 000000001111111111 dan 4 terakhir dapat dipahami sebagai 0123012301230123 apa yang terjadi dengan yang di tengah?
PirateApp
3
Cara mudah untuk memikirkan ini adalah bahwa numpy bekerja persis seperti yang diharapkan di sini, tetapi pencetakan tuple Python bisa menyesatkan. Dalam (R, )kasus ini, bentuknya ndarrayadalah tuple dengan elemen tunggal, sehingga dicetak oleh Python dengan tanda koma. Tanpa koma ekstra, itu akan menjadi ambigu dengan ekspresi dalam tanda kurung . A ndarraydengan dimensi tunggal dapat berupa sebagai vektor panjang kolom R. Dalam (R, 1)kasus, tupel memiliki dua elemen, sehingga dapat dianggap sebagai vektor baris (atau matriks dengan 1 baris panjang R.
Michael Yang
1
@ Alex-droidAD: Lihat pertanyaan ini dan jawabannya.
Gareth Rees
16

Perbedaan antara (R,)dan (1,R)secara harfiah jumlah indeks yang perlu Anda gunakan. ones((1,R))adalah array 2-D yang kebetulan hanya memiliki satu baris. ones(R)adalah vektor. Umumnya jika tidak masuk akal untuk variabel memiliki lebih dari satu baris / kolom, Anda harus menggunakan vektor, bukan matriks dengan dimensi tunggal.

Untuk kasus spesifik Anda, ada beberapa opsi:

1) Buat argumen kedua sebagai vektor. Berikut ini berfungsi dengan baik:

    np.dot(M[:,0], np.ones(R))

2) Jika Anda ingin operasi matlab seperti matriks, gunakan kelas matrixbukan ndarray. Semua matrik dipaksa menjadi array 2-D, dan operator *melakukan perkalian matriks alih-alih elemen (sehingga Anda tidak perlu titik). Dalam pengalaman saya, ini lebih banyak masalah daripada nilainya, tetapi mungkin lebih baik jika Anda terbiasa dengan matlab.

Evan
sumber
Iya. Saya mengharapkan perilaku yang lebih seperti matlab. Saya akan melihat matrixkelas. Apa masalah untuk matrixkelas BTW?
clwen
2
Masalahnya matrixadalah itu hanya 2D, dan juga karena itu membebani operator '*', fungsi yang ditulis untuk ndarraymungkin gagal jika digunakan pada a matrix.
Evan
11

Bentuknya adalah tuple. Jika hanya ada 1 dimensi bentuknya akan menjadi satu angka dan kosong setelah koma. Untuk 2+ dimensi, akan ada angka setelah semua koma.

# 1 dimension with 2 elements, shape = (2,). 
# Note there's nothing after the comma.
z=np.array([  # start dimension
    10,       # not a dimension
    20        # not a dimension
])            # end dimension
print(z.shape)

(2,)

# 2 dimensions, each with 1 element, shape = (2,1)
w=np.array([  # start outer dimension 
    [10],     # element is in an inner dimension
    [20]      # element is in an inner dimension
])            # end outer dimension
print(w.shape)

(2,1)

Katie Jergens
sumber
5

Untuk kelas array dasarnya, array 2d tidak lebih spesial daripada 1d atau 3d. Ada beberapa operasi yang mempertahankan dimensi, beberapa yang menguranginya, lainnya menggabungkan atau bahkan memperluasnya.

M=np.arange(9).reshape(3,3)
M[:,0].shape # (3,) selects one column, returns a 1d array
M[0,:].shape # same, one row, 1d array
M[:,[0]].shape # (3,1), index with a list (or array), returns 2d
M[:,[0,1]].shape # (3,2)

In [20]: np.dot(M[:,0].reshape(3,1),np.ones((1,3)))

Out[20]: 
array([[ 0.,  0.,  0.],
       [ 3.,  3.,  3.],
       [ 6.,  6.,  6.]])

In [21]: np.dot(M[:,[0]],np.ones((1,3)))
Out[21]: 
array([[ 0.,  0.,  0.],
       [ 3.,  3.,  3.],
       [ 6.,  6.,  6.]])

Ekspresi lain yang memberikan array yang sama

np.dot(M[:,0][:,np.newaxis],np.ones((1,3)))
np.dot(np.atleast_2d(M[:,0]).T,np.ones((1,3)))
np.einsum('i,j',M[:,0],np.ones((3)))
M1=M[:,0]; R=np.ones((3)); np.dot(M1[:,None], R[None,:])

MATLAB dimulai dengan hanya array 2D. Versi yang lebih baru memungkinkan lebih banyak dimensi, tetapi mempertahankan batas bawah 2. Tetapi Anda masih harus memperhatikan perbedaan antara matriks baris dan satu kolom, satu dengan bentuk (1,3)v (3,1). Seberapa sering Anda menulis [1,2,3].'? Saya akan menulis row vectordan column vector, tetapi dengan kendala 2d itu, tidak ada vektor di MATLAB - setidaknya tidak dalam pengertian matematika vektor sebagai 1d.

Sudahkah Anda melihat np.atleast_2d(juga versi _1d dan _3d)?

hpaulj
sumber
1

1) Alasan untuk tidak memilih bentuk (R, 1)over (R,)adalah karena hal itu tidak perlu mempersulit. Selain itu, mengapa lebih disukai memiliki bentuk (R, 1)secara default untuk vektor panjang-R daripada (1, R)? Lebih baik untuk tetap sederhana dan eksplisit ketika Anda membutuhkan dimensi tambahan.

2) Sebagai contoh, Anda menghitung produk luar sehingga Anda dapat melakukan ini tanpa reshapepanggilan dengan menggunakan np.outer:

np.outer(M[:,0], numpy.ones((1, R)))
bogatron
sumber
Terima kasih atas jawabannya. 1) M[:,0]pada dasarnya mendapatkan semua baris dengan elemen pertama, jadi lebih masuk akal untuk memiliki (R, 1)daripada (1, R). 2) Ini tidak selalu dapat diganti dengan np.outer, misalnya, titik untuk matriks dalam bentuk (1, R) lalu (R, 1).
clwen
1) Ya, itu bisa jadi konvensi tetapi itu membuatnya kurang nyaman dalam keadaan lain. Konvensi juga bisa untuk M [1, 1] untuk mengembalikan bentuk (1, 1) array tetapi itu juga biasanya kurang nyaman daripada skalar. Jika Anda benar-benar menginginkan perilaku seperti matriks, maka Anda akan lebih baik menggunakan matrixobjek. 2) Sebenarnya, np.outerbekerja terlepas dari apakah bentuk yang (1, R), (R, 1), atau kombinasi dari keduanya.
bogatron
0

Sudah banyak jawaban bagus di sini. Tetapi bagi saya itu sulit untuk menemukan beberapa contoh, di mana bentuk atau array dapat merusak semua program.

Jadi ini dia:

import numpy as np
a = np.array([1,2,3,4])
b = np.array([10,20,30,40])


from sklearn.linear_model import LinearRegression
regr = LinearRegression()
regr.fit(a,b)

Ini akan gagal dengan kesalahan:

ValueError: Diharapkan array 2D, sebaliknya mendapat array 1D

tetapi jika kita menambahkan reshapeke a:

a = np.array([1,2,3,4]).reshape(-1,1)

ini bekerja dengan benar!

Mikhail_Sam
sumber