2016-09-10 6 views
2

У меня есть Vector от Vector s различной длины W. Эти последние векторы содержат целые числа от 0 до 150 000 с шагом 5, но также могут быть пустыми. Я пытаюсь вычислить эмпирический cdf для каждого из этих векторов. Я мог бы вычислить эти CDF итерация каждый вектор и каждое целое число, как этотвырывание петли в Julia

cdfdict = Dict{Tuple{Int,Int},Float64}() 
for i in 1:length(W) 
    v = W[i] 
    len = length(v) 
    if len == 0 
     pcdf = 1.0 
    else 
     for j in 0:5:150_000 
      pcdf = length(v[v .<= j])/len 
      cdfdict[i, j] = pcdf 
     end 
    end 
end 

Однако, такой подход является неэффективным, поскольку CDF будет равна 1 для j >= maximum(v) и иногда это maximum(v) будет намного ниже, чем 150,000.

Мой вопрос: как я могу включать в себя условия, нарушающего из j петли для j > maximum(v), но по-прежнему назначает pcdf = 1.0 для остальной части j с?

Я попытался включить break, когда j > maximum(v), но это, конечно, останавливает цикл от остальной части j. Кроме того, я могу разбить цикл, а затем использовать get! для доступа/включения 1.0 для ключей, которые не найдены в cdfdict позже, но это не то, что я ищу.

+3

вы можете инициализируются ваш cdfdict с '1' в качестве значения по умолчанию, так что даже если вы сломаете это неважно. –

+0

это почти то же самое, что использовать 'get!' После цикла ... – amrods

+1

Я думаю, что решение @ TasosPapastylianou лучше. Но вы можете недооценивать, насколько быстрые петли в Джулии. Зацикливание от 0 до 150 000 на 5 секунд и заполнение заданной величиной 1,0 займет тривиальное количество времени. –

ответ

2

Выработать на мой комментарий, этот ответ детализирует реализацию, которая заполняет массив вместо Dict.

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

W = [rand(0:mv,rand(0:10)) for mv in floor(Int,exp(log(150_000)*rand(10)))] 

Следующая создать массив нужного размера, наполненную: 1,0 сек

cdfmat = ones(Float64,length(W),length(0:5:150_000)); 

Теперь, чтобы заполнить начала CDFs:

for i=1:length(W) 
    v = sort(W[i]) 
    k = 1 
    thresh = 0 
    for j=1:length(v) 
     if (j>1 && v[j]==v[j-1]) 
      continue 
     end 
     pcdf = (j-1)/length(v) 
     while thresh<v[j] 
      cdfmat[i,k]=pcdf 
      k += 1 
      thresh += 5 
     end 
    end 
end 

В этой реализации используется sort, который иногда может быть медленным, но другой имп lementations в основном сравнивают вектор с различными значениями, которые в большинстве случаев еще более медленны.

+0

Этот ответ немного боком из вопроса и не объясняется подробно, поэтому при необходимости я подробно отредактирую по вопросам и вопросам в комментариях. –

+0

Как насчет случаев, когда 'v' содержит повторяющиеся значения? – amrods

+0

Я думаю, что это работает, спасибо. – amrods

2

break только на одном уровне. Вы можете сделать то, что хотите, обернув функцию цикла for и используя return (вместо того, где вы бы положили разрыв), или используя @goto.

Или где вы могли бы сломаться, вы могли бы переключить логическое значение breakd=true, а затем сломать, а в нижней части большего цикла сделать if breakd break end.

+2

'@ goto' гораздо яснее, чем' if breakd', и должно быть настоятельно предпочтительным. –

2

Вы можете использовать другой цикл for, чтобы установить все остальные элементы в 1.0. Внутренний цикл становится

m = maximum(v) 
for j in 0:5:150_000 
    if j > m 
     for k in j:5:150_000 
      cdfdict[i, k] = 1.0 
     end 
     break 
    end 
    pcdf = count(x -> x <= j, v)/len 
    cdfdict[i, j] = pcdf 
end 

Однако это довольно сложно понять. Было бы проще использовать ветку. На самом деле это должно быть так же быстро, потому что отрасль очень предсказуема.

m = maximum(v) 
for j in 0:5:150_000 
    if j > m 
     cdfdict[i, j] = 1.0 
    else 
     pcdf = count(x -> x <= j, v)/len 
     cdfdict[i, j] = pcdf 
    end 
end 
1

Еще один ответ дал реализацию с использованием массива, который вычислял CDF, сортируя образцы и заполняя ячейки CDF квантовыми значениями. Поскольку весь массив заполнен таким образом, выполнение другого прохода по массиву не должно быть чрезмерно дорогостоящим (мы терпим один проход уже). Бит сортировки и сопровождающее его распределение можно избежать, вычислив гистограмму в массиве и используя cumsum для создания CDF.Возможно, этот код будет объяснить это лучше:

Initialize размеров, длины и ширины:

n = 10; w = 5; rmax = 150_000; hl = length(0:w:rmax) 

произвести образец пример:

W = [rand(0:mv,rand(0:10)) for mv in floor(Int,exp(log(rmax)*rand(n)))]; 

Вычислить ВРР:

cdfmat = zeros(Float64,n,hl); # empty histograms 
for i=1:n      # drop samples into histogram bins 
    for j=1:length(W[i]) 
    cdfmat[i,1+(W[i][j]+w-1)÷5]+=one(Float64) 
    end 
end 
cumsum!(cdfmat,cdfmat,2)  # calculate pre-CDF by cumsum 
for i=1:n      # normalize each CDF by total 
    if cdfmat[i,hl]==zero(Float64) # check if histogram empty? 
    for j=1:hl     # CDF of 1.0 as default (might be changed) 
     cdfmat[i,j] = one(Float64) 
    end 
    else       # the normalization factor calc-ed once 
    f = one(Float64)/cdfmat[i,hl] 
    for j=1:hl 
     cdfmat[i,j] *= f 
    end 
    end 
end 

(a) Обратите внимание на использование one, zero для подготовки к изменение типа Real - это хорошая практика. (b) Также необходимо дополнительно оптимизировать добавление различных @inbounds и @simd. (c) Рекомендуется использовать этот код в функции (это не делается в этом ответе). (d) Если нулевой CDF для пустых выборок является ОК (это означает, что никакие сэмплы не означают огромные семплы семантически), то второй for может быть упрощен.

другие ответы больше вариантов, и напоминание: Преждевременная оптимизация есть корень всех зол (Кнут ??)