Salin atau tidak salin, itu pertanyaannya – Beragampengetahuan
Oleh: Blog Bogumił Kamiński
Repost dari:
Saya telah menggunakan Julia selama beberapa waktu sekarang.
Jadi saya pikir saya sudah terbiasa menggunakan bahasa ini dengan aman.
Sayangnya, itu tidak sepenuhnya benar.
Hari ini saya ingin berbagi dengan Anda pemikiran saya tentang template penting yang saya butuhkan
belajar kembali secara terus menerus.
Seperti yang Anda ketahui, pengembang paket di Julia ingin mereka cepat. Untuk ini
alasan mereka biasanya mengoptimalkannya. Salah satu pengoptimalan umum adalah menghindari
alokasi yang tidak perlu. Pendekatan ini masuk akal, tetapi berisiko tidak diinginkan
mutasi data tersebut.
Secara umum, Anda memiliki dua situasi ketika Anda mungkin memiliki masalah
mutasi:
- saat Anda meneruskan data ke fungsi yang dapat mengubahnya;
- ketika Anda mendapatkan data yang dikembalikan dari fungsi dan kemudian Anda mengubahnya
Pada saat yang sama, operasi ini mengubah beberapa data lain yang belum Anda miliki
diharapkan untuk diubah.
Skenario pertama ditangani dengan cukup jelas di Julia. Fungsi apapun
dapat mengubah argumennya dengan konvensi, jadi namanya diakhiri dengan !.
Ini dijelaskan dengan jelas dalam manual Julia di
menambahkan ! ke nama fungsi yang mengubah bagian argumennya.
Kasus kedua kurang jelas dan menjadi fokus postingan saya hari ini. saya akan menjelaskan
itu menggunakan dua contoh yang diambil dari pertanyaan nyata yang diajukan oleh pengguna Julia minggu lalu.
Posting ditulis di bawah Julia 1.9.0-rc1, DataFrames.jl 1.5.0, GLM.jl 1.8.1 dan
ShiftedArrays.jl 2.0.0.
Pertimbangkan kode berikut:
julia> using DataFrames
julia> using Random
julia> using ShiftedArrays: lag
julia> Random.seed!(1234);
julia> df = DataFrame(x = rand(10))
10×1 DataFrame
Row │ x
│ Float64
─────┼───────────
1 │ 0.579862
2 │ 0.411294
3 │ 0.972136
4 │ 0.0149088
5 │ 0.520355
6 │ 0.639562
7 │ 0.839622
8 │ 0.967143
9 │ 0.131026
10 │ 0.946453
julia> df.xlag = lag(df.x)
10-element ShiftedVector{Float64, Missing, Vector{Float64}}:
missing
0.5798621201341324
0.4112941179498505
0.9721360824554687
0.014908849285099945
0.520354993723718
0.6395615996802734
0.8396219340580711
0.967142768915383
0.13102565622085904
julia> df
10×2 DataFrame
Row │ x xlag
│ Float64 Float64?
─────┼────────────────────────────
1 │ 0.579862 missing
2 │ 0.411294 0.579862
3 │ 0.972136 0.411294
4 │ 0.0149088 0.972136
5 │ 0.520355 0.0149088
6 │ 0.639562 0.520355
7 │ 0.839622 0.639562
8 │ 0.967143 0.839622
9 │ 0.131026 0.967143
10 │ 0.946453 0.131026
Jebakan potensial adalah :xlag kolom adalah tampilan. Jika kita memodifikasi :x Tetapi juga :xlag akan dimodifikasi.
Ini contohnya:
julia> df.x[1] = 0.0
0.0
julia> df
10×2 DataFrame
Row │ x xlag
│ Float64 Float64?
─────┼────────────────────────────
1 │ 0.0 missing
2 │ 0.411294 0.0
3 │ 0.972136 0.411294
4 │ 0.0149088 0.972136
5 │ 0.520355 0.0149088
6 │ 0.639562 0.520355
7 │ 0.839622 0.639562
8 │ 0.967143 0.839622
9 │ 0.131026 0.967143
10 │ 0.946453 0.131026
Dalam beberapa kasus, Anda mungkin menginginkannya secara nyata. Tetapi dalam banyak kasus ini akan menyebabkan kesalahan,
terutama jika kedua objek digunakan di bagian kode yang berbeda. Di sini saya menyimpannya
bingkai data karena ini adalah cara mudah untuk menunjukkan apa yang terjadi.
Jebakan lainnya adalah saat Anda mencoba mengubah kerangka data seperti itu dengan mendorong baris ke dalamnya:
julia> push!(df, (10.0, 11.0))
┌ Error: Error adding value to column :xlag. Maybe it was forgotten to ask for column element type promotion, which can be done by passing the promote=true keyword argument.
Seperti yang Anda lihat, operasi gagal karena kami memiliki tampilan yang dihosting
sebagai kolom kerangka data dan tampilan tidak dapat diubah ukurannya.
Ini sepertinya kesalahan sederhana, tetapi kenyataannya, pengguna melaporkan telah melakukannya.
Cara paling sederhana untuk mengatasi masalah ini adalah memastikan bahwa Anda copy pendapat:
julia> df.xlag = copy(lag(df.x))
10-element Vector{Union{Missing, Float64}}:
missing
0.0
0.4112941179498505
0.9721360824554687
0.014908849285099945
0.520354993723718
0.6395615996802734
0.8396219340580711
0.967142768915383
0.13102565622085904
julia> push!(df, (10.0, 11.0))
11×2 DataFrame
Row │ x xlag
│ Float64 Float64?
─────┼─────────────────────────────
1 │ 0.0 missing
2 │ 0.411294 0.0
3 │ 0.972136 0.411294
4 │ 0.0149088 0.972136
5 │ 0.520355 0.0149088
6 │ 0.639562 0.520355
7 │ 0.839622 0.639562
8 │ 0.967143 0.839622
9 │ 0.131026 0.967143
10 │ 0.946453 0.131026
11 │ 10.0 11.0
Biarkan saya mengilustrasikan jebakan lain dengan model linier. Mulailah dengan membuat kumpulan data sederhana:
julia> Random.seed!(1234);
julia> df = DataFrame(randn(10, 3), [:x1, :x2, :error]);
julia> df.y = df.x1 + df.x2 + df.error;
julia> df.wts = rand(10);
julia> df
10×5 DataFrame
Row │ x1 x2 error y wts
│ Float64 Float64 Float64 Float64 Float64
─────┼───────────────────────────────────────────────────────────
1 │ 0.970656 0.705993 -1.0565 0.620151 0.840641
2 │ -0.979218 1.09156 0.148361 0.260698 0.523948
3 │ 0.901861 0.871498 -1.83851 -0.0651514 0.0128461
4 │ -0.0328031 0.0856911 -1.07363 -1.02074 0.40573
5 │ -0.600792 0.960079 -0.20563 0.153657 0.124074
6 │ -1.44518 0.907837 0.770703 0.233363 0.648874
7 │ 2.70742 -1.46506 -1.21394 0.0284219 0.574296
8 │ 1.52445 -0.215859 1.0915 2.40009 0.408537
9 │ 0.759804 0.575094 1.22004 2.55494 0.0731709
10 │ -0.881437 -1.79563 -0.0758316 -2.7529 0.501756
Sekarang paskan model linier berbobot:
julia> using GLM
julia> model = lm(@formula(y ~ x1 + x2), df, wts=df.wts)
StatsModels.TableRegressionModel{LinearModel{GLM.LmResp{Vector{Float64}}, GLM.DensePredChol{Float64, LinearAlgebra.CholeskyPivoted{Float64, Matrix{Float64}, Vector{Int64}}}}, Matrix{Float64}}
y ~ 1 + x1 + x2
Coefficients:
─────────────────────────────────────────────────────────────────────────
Coef. Std. Error t Pr(>|t|) Lower 95% Upper 95%
─────────────────────────────────────────────────────────────────────────
(Intercept) -0.163691 0.721581 -0.23 0.8550 -7.38861 7.06123
x1 0.698275 0.554725 1.26 0.4108 -4.85598 6.25253
x2 1.03178 0.732565 1.41 0.3753 -6.30312 8.36668
─────────────────────────────────────────────────────────────────────────
Misalkan kita menulis fungsi sederhana untuk menghitung varians-kovarians
matriks koefisien model kami:
function our_vcov(model)
X = modelmatrix(model)
u² = residuals(model) .^ 2
wts = model.model.rr.wts
X .*= sqrt.(wts)
u² .*= wts
dfr = dof_residual(model)
M = inv(X'*X)
return M * sum(u²) / dfr
end
Mari kita periksa apakah kita mendapatkan hasil yang sama seperti yang dikembalikan oleh integrasi vcov fungsi:
julia> vcov(model)
3×3 Matrix{Float64}:
0.520679 -0.0861524 -0.0613806
-0.0861524 0.30772 0.168677
-0.0613806 0.168677 0.536652
julia> our_vcov(model)
3×3 Matrix{Float64}:
0.520679 -0.0861524 -0.0613806
-0.0861524 0.30772 0.168677
-0.0613806 0.168677 0.536652
Semua terlihat bagus. Mari kita jalankan kembali fungsi kita:
julia> our_vcov(model)
3×3 Matrix{Float64}:
0.954193 -0.196279 -0.191441
-0.196279 0.534882 0.295785
-0.191441 0.295785 0.965165
Tunggu – kali ini kami memiliki hasil yang berbeda. Apa masalahnya?
Masalahnya adalah dengan X .*= sqrt.(wts) operasi pembaruan X di tempat.
Dan itu berhasil X = modelmatrix(model) kami mengikat X nilai yang
disimpan secara internal di model Benda. Jadi kami, tanpa sadar berubahmodel.
Dalam hal ini, perbaikan akan menjadi contoh untuk menulis X = X .* sqrt.(wts) atauX = copy(modelmatrix(model)).
Masalahnya adalah sangat mudah untuk melupakannya. Keduanya X = modelmatrix(model)
Dan X .*= sqrt.(wts) karya yang terlihat alami dan dapat dengan mudah digunakan bersama.
Juga jika Anda melihat modelmatrix membantu:
help?> modelmatrix
search: modelmatrix ModelMatrix
modelmatrix(model::RegressionModel)
Return the model matrix (a.k.a. the design matrix).
tidak ada yang memperingatkan Anda bahwa operasi dapat mengembalikan beberapa nilai yang disimpan model
atau salinannya.
Catatan untuk pengembang paket: jenis kesalahan ini tidak mungkin terdeteksi oleh pengujian standar
karena hanya menjadi jelas setelah Anda menjalankan operasi dua kali pada beberapa objek (dan seringkali
tes dijalankan hanya sekali).
Jadi pola apa yang perlu saya pelajari kembali? Aturan emasnya adalah:
Selalu periksa apakah nilai yang dikembalikan oleh beberapa fungsi baru dialokasikan.
Sayangnya, seperti yang Anda lihat dari contoh yang saya berikan, mudah lupa untuk memverifikasi ini.
Masalah utamanya adalah tidak ada bantuan visual yang dapat membantu Anda mengidentifikasi masalah tersebut
(berlawanan dengan fungsi yang mengubah argumennya yang diakhiri dengan .) !).
Jika Anda adalah pengguna DataFrames.jl, Anda terlindungi sebagian dari risiko yang saya bahas hari ini.
Fungsi yang biasa digunakan seperti select, combine, transformatau DataFrame konstruktor
pastikan untuk membuat salinan kolom dari kerangka data yang mereka kembalikan.
Memang, ini mengurangi kinerjanya, tetapi membantu memastikan keamanan dan biasanya perolehan kinerjanya minimal.
(dan untuk pengguna yang terobsesi dengan kecepatan, kami punya copycols=false argumen kata kunci atau fungsi dengan! bekerja di lokasi).
Jadi jawaban saya untuk pertanyaan dari judul posting adalah: Menyalin (kecuali jika Anda yakin bisa
lewati penyalinan dengan aman, dan manfaat kinerjanya sangat berharga).
Terkait
Software Terbaru Saat Ini
Aplikasi yang sedang trend saat ini
object oriented programming, programming language, programming adalah, web programming, belajar programming, tournament software, software, software adalah, contoh software, apa itu software, pengertian software, aplikasi, aplikasi penghasil uang, aplikasi bokep, aplikasi video, programming
#Salin #atau #tidak #salin #itu #pertanyaannya