Как конвертировать разные виды многомерных массивов в Julia - PullRequest
3 голосов
/ 16 октября 2019

Я переезжаю с питона / numpy на джулию. Я действительно смущен многомерными массивами Джулии, и мне кажется, что есть некоторый дополнительный уровень сложности / хлопот (по сравнению с numpy).

Существует различие между 1) векторными строками, 2) векторными столбцами, 3) многомерными массивами и 4) вложенными (= массивами массивов). Это было бы хорошо (возможно, полезно для оптимизации производительности), предполагая, что существует простой способ преобразования между ними. Но я не могу понять, как это сделать.

Простой пример: Я просто пытаюсь сгенерировать 2D прямоугольную сетку точек и построить их


ps = [ [ix*0.1 iy*0.1] for ix=1:10, iy=1:10 ]
# 10×10 Array{Array{Float64,2},2}:
# Oh, this is nested array? I wand just simple 3D array 10x10x2

scatter( ps[:,:,1], ps[:,:,2], markersize = 2, markerstrokewidth = 0, aspect_ratio=:equal )
# ERROR: BoundsError: attempt to access 10×10 Array{Array{Float64,2},2} at index [Base.Slice(Base.OneTo(10)), Base.Slice(Base.OneTo(10)), 2]

sh = size(ps)
# (10,10)

ps = reshape( ps, ( sh[1]*sh[2],2) )
# ERROR: DimensionMismatch("new dimensions (100, 2) must be consistent with  array size 100")
# Oh dear :(

ps = reshape( ps, ( sh[1]*sh[2],:) )
# 100×1 Array{Array{Float64,2},2}

xs = ps[:,1]
# 100-element Array{Array{Float64,2},1}
# ??? WTF? ... this arrays looks like whole 'ps' 
ys = ps[:,2] 
# ERROR: BoundsError: attempt to access 100×1 Array{Array{Float64,2},2} at index [Base.Slice(Base.OneTo(100)), 2]

xs = ps[:][1]
# 1×2 Array{Float64,2}: 
#  0.1  0.1
#  But I want all xs  (ps[:,1]), not (ps[1,:]) 

# Let's try some broadcasting
xs = ps.[1]
# ERROR: syntax: invalid syntax "ps.[1]"
xs = .ps[1]
# ERROR: syntax: invalid identifier name "."


# Perhaps transpose will help?
ps_ = ps'   #' stackoverflow syntax highlighting for Julia is broken ?
# 1×100 LinearAlgebra.Adjoint{LinearAlgebra.Adjoint{Float64,Array{Float64,2}},Array{Array{Float64,2},2}}:
# OMG! ... That is even worse

scatter( ps[:,1], ps[:,2], markersize = 2, markerstrokewidth = 0, aspect_ratio=:equal )
# Nope

ОК, это как-тоработает. Но все же мне нужно выяснить, как конвертировать различные формы массивов выше

using Plots
ps = [ [ix*0.1 iy*0.1] for ix=1:10, iy=1:10 ]
ps = vcat(ps...)
xs = ps[:,1]
ys = ps[:,2]
scatter( xs, ys, markersize = 2, markerstrokewidth = 0, aspect_ratio=:equal )

РЕДАКТИРОВАТЬ:

Может быть, было бы неплохо перечислить некоторые учебники, где я искал ответ раньшеЯ спросил:

Ответы [ 3 ]

3 голосов
/ 16 октября 2019

Джулия работает в мажоре столбцов, поэтому основным вектором является вектор столбцов. Чтобы преобразовать вектор столбца в вектор строки, используйте permutedims(colvec). Чтобы преобразовать вектор строки в вектор столбца, используйте permutedims(rowvec).

julia> colvec = [1, 2, 3]
3-element Array{Int64,1}:
 1
 2
 3

julia> rowvec = permutedims(colvec)
1×3 Array{Int64,2}:
 1  2  3

julia> permutedims(rowvec)
3×1 Array{Int64,2}:
 1
 2
 3

Чтобы преобразовать матрицу (или любой двумерный массив) в вектор столбца, используйте vec. Поскольку Юлия хранит двумерные массивы по столбцам, это будет проходить по каждому столбцу по очереди. Обратите внимание, что размеры двумерного массива отображаются как <rows>x<cols> Array{<type>,2}.

julia> matrix = [1 4 
                 2 5 
                 3 6]
3×2 Array{Int64,2}:
 1  4
 2  5
 3  6  

julia> colvec = vec(matrix)
6-element Array{Int64,1}:
 1
 2
 3
 4
 5
 6

Чтобы преобразовать этот colvec обратно в исходный массив, необходимо знать размеры этого исходного массива. ndims(x) считает размеры x, а size(x) - количество элементов в каждом измерении x.

julia> reshape(colvec, size(matrix))
3×2 Array{Int64,2}:
 1  4
 2  5
 3  6

Вы можете транспонировать записи с помощью permutedims(matrix).

julia> matrix
3×2 Array{Int64,2}:
 1  4
 2  5
 3  6

julia> permutedims(matrix)
2×3 Array{Int64,2}:
 1  2  3
 4  5  6
* 1020. * Те же принципы применимы к массивам более высокой размерности.
array = reshape(collect(1:12), (3, 2, 2))
3×2×2 Array{Int64,3}:
[:, :, 1] =
 1  4
 2  5
 3  6

[:, :, 2] =
 7  10
 8  11
 9  12

julia> vec(array)
12-element Array{Int64,1}:
  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12

Для работы с вложенными массивами я предлагаю использовать RecursiveArrayTools.jl .

2 голосов
/ 16 октября 2019

В вышеприведенном коде довольно много, но только внимание сосредоточено на вашей точке отправления и ожидаемом результате:

Почему вложенный массив?

Ваше понимание создаетмассив [ix*0.1 iy*0.1] для каждой комбинации ix и iy, поэтому я бы сказал, что вы явно просили об этом.

Вероятно, есть несколько причудливых способов сделать это с причудливым пониманием или как-то сгладитьвложенный массив, но в таких случаях, как этот, я хотел бы четко указать, что я пытаюсь достичь:

ps = zeros(10,10,2) # 10x10x2 Array{Float64,3}
for ix = 1:10, iy = 1:10
        ps[ix, iy, :] = [ix*0.1 iy*0.1]
end

Если речь идет об однострочном выражении, вы можете рассмотреть создание обоих массивов 10x10 в пониманияха затем объединить их по третьему измерению:

ps = cat([ix*0.1 for ix=1:10, iy=1:10], [iy*0.1 for ix=1:10, iy=1:10], dims = 3)
0 голосов
/ 17 октября 2019

Другие дали ответы, которые помогут решить вашу проблему, поэтому я бы лучше просто рассмотрел ваш пример и попытался объяснить, что происходит. Ты выглядишь довольно разочарованным, что не редкость при изучении нового языка, но большинство твоих жалоб похоже на то, что Джулия последовательна.

ps = [ [ix*0.1 iy*0.1] for ix=1:10, iy=1:10 ]
# 10×10 Array{Array{Float64,2},2}:
# Oh, this is nested array? I wand just simple 3D array 10x10x2

Здесь вы создаете массив 10x10, где каждый элементматрица 1x2. Это то, что вы просите, это не Джулия, которая трудна или неясна, она просто последовательна и прямолинейна.

scatter( ps[:,:,1], ps[:,:,2], markersize = 2, markerstrokewidth = 0, aspect_ratio=:equal )
# ERROR: BoundsError: attempt to access 10×10 Array{Array{Float64,2},2} at index [Base.Slice(Base.OneTo(10)), Base.Slice(Base.OneTo(10)), 2]

У вас есть двумерный массив, поэтому вы не можете индексировать в нем 3 индекса.

sh = size(ps)
# (10,10)

ps = reshape( ps, ( sh[1]*sh[2],2) )
# ERROR: DimensionMismatch("new dimensions (100, 2) must be consistent with  array size 100")
# Oh dear :(

У вас есть массив 10x10 и попытайтесь преобразовать его в массив 100x2. В новом массиве будет 200 элементов, что в два раза больше исходного, поэтому это не сработает.

ps = reshape( ps, ( sh[1]*sh[2],:) )
# 100×1 Array{Array{Float64,2},2}

Здесь вы преобразуете его в массив 100x1, это нормально.

xs = ps[:,1]
# 100-element Array{Array{Float64,2},1}
# ??? WTF? ... this arrays looks like whole 'ps' 

А теперь вы просите первый (и единственный) столбец нового, измененного ps. Итак, естественно, вы получаете все данные. Обратите внимание, что xs теперь является одномерным массивом, а не двумерным массивом 100x1.

ys = ps[:,2] 
# ERROR: BoundsError: attempt to access 100×1 Array{Array{Float64,2},2} at index [Base.Slice(Base.OneTo(100)), 2]

Вы запрашиваете второй столбец массива 100x1.

xs = ps[:][1]
# 1×2 Array{Float64,2}: 
#  0.1  0.1
#  But I want all xs  (ps[:,1]), not (ps[1,:]) 

ps[:]превращает ps в одномерный вектор с 100 элементами, а затем вы запрашиваете первый элемент этого. Мне кажется, ожидаемое поведение.

# Let's try some broadcasting
xs = ps.[1]
# ERROR: syntax: invalid syntax "ps.[1]"

Да, это не работает, и вполне разумно ожидать, что это возможно, но это возможная будущая функция. Возможно, вы ищете first.(ps), который читает первый элемент из каждого элемента ps. Точно так же last.(ps) читает последний элемент из каждого элемента ps.

xs = .ps[1]
# ERROR: syntax: invalid identifier name "."

Это недопустимый синтаксис. Точечный синтаксис работает только для функций и операторов.

# Perhaps transpose will help?
ps_ = ps'   #' stackoverflow syntax highlighting for Julia is broken ?
# 1×100 LinearAlgebra.Adjoint{LinearAlgebra.Adjoint{Float64,Array{Float64,2}},Array{Array{Float64,2},2}}:
# OMG! ... That is even worse

Не уверен, что вы хотите, чтобы здесь произошло. Transpose возвращает ленивый тип данных по соображениям производительности. Это довольно аккуратно.

scatter( ps[:,1], ps[:,2], markersize = 2, markerstrokewidth = 0, aspect_ratio=:equal )
# Nope

Насколько я помню, вы изменили ps в массив 100x1, поэтому ps[:,2] не может работать.

...