variable - subplot title matlab
Repita los elementos del vector (3)
Primero piense en formar un vector de índice [1 1 1 1 1 2 2 3 3 3 4 4 5]
. Ver los incrementos regulares aquí me hace pensar en cumsum: podemos obtener estos pasos colocando los que están en la ubicación correcta en un vector de ceros: [1 0 0 0 0 1 0 1 0 0 1 0 1]
. Y que podemos obtener ejecutando otro cumsum
en la lista de entrada. Después de ajustar por las condiciones finales y la indexación basada en 1, obtenemos esto:
B(cumsum(r) + 1) = 1;
idx = cumsum(B) + 1;
idx(end) = [];
A(idx)
Esta pregunta ya tiene una respuesta aquí:
Tengo un vector de valor A
contiene elementos i
, por ejemplo:
A = [0.1 0.2 0.3 0.4 0.5];
y di r = [5 2 3 2 1];
Ahora quiero crear un nuevo vector Anew
contenga r(i)
repeticiones de los valores i
en A
, tal que los primeros r(1)=5
ítems en Anew
tienen valor A(1)
y la longitud del nuevo vector es sum(r)
. Así:
Anew = [0.1 0.1 0.1 0.1 0.1 0.2 0.2 0.3 0.3 0.3 0.4 0.4 0.5]
Estoy seguro de que esto se puede hacer con una combinación complicada for
bucle, por ejemplo repmat
, pero ¿hay alguna posibilidad de que alguien sepa cómo hacerlo de una manera más suave?
bsxfun
basado en bsxfun
-
A = [0.1 0.2 0.3 0.4 0.5]
r = [5 2 3 2 1]
repeats = bsxfun(@le,[1:max(r)]'',r) %//'' logical 2D array with ones in each column
%// same as the repeats for each entry
A1 = A(ones(1,max(r)),:) %// 2D matrix of all entries repeated maximum r times
%// and this resembles your repmat
out = A1(repeats) %// desired output with repeated entries
En esencia, podría convertirse en un juego de dos líneas
A1 = A(ones(1,max(r)),:);
out = A1(bsxfun(@le,[1:max(r)]'',r));
Salida -
out =
0.1000
0.1000
0.1000
0.1000
0.1000
0.2000
0.2000
0.3000
0.3000
0.3000
0.4000
0.4000
0.5000
Benchmarking
Algunos resultados de referencia podrían producirse para las soluciones presentadas hasta ahora.
Código de Benchmarking - Caso I
%// Parameters and input data
N = 4000;
max_repeat = 4000;
A = rand(1,N);
r = randi(max_repeat,1,N);
num_runs = 10; %// no. of times each solution is repeated for better benchmarking
disp(''------------------- With arrayfun'')
tic
for k1 = 1:num_runs
Anew = arrayfun(@(x) repmat(A(x), r(x), 1), 1:numel(A), ''uni'', 0);
Anew = vertcat(Anew{:});
end
toc, clear Anew
disp(''------------------- With cumsum'')
tic
for k1 = 1:num_runs
B(cumsum(r) + 1) = 1;
idx = cumsum(B) + 1;
idx(end) = [];
out1 = A(idx);
end
toc,clear B idx out1
disp(''------------------- With bsxfun'')
tic
for k1 = 1:num_runs
A1 = A(ones(1,max(r)),:);
out2 = A1(bsxfun(@le,[1:max(r)]'',r));
end
toc
Resultados
------------------- With arrayfun
Elapsed time is 2.198521 seconds.
------------------- With cumsum
Elapsed time is 5.360725 seconds.
------------------- With bsxfun
Elapsed time is 2.896414 seconds.
Código de Benchmarking - Caso II [Mayor tamaño de datos pero menor de r]
%// Parameters and input data
N = 10000;
max_repeat = 1000;
Resultados
------------------- With arrayfun
Elapsed time is 2.641980 seconds.
------------------- With cumsum
Elapsed time is 3.426921 seconds.
------------------- With bsxfun
Elapsed time is 1.858007 seconds.
Conclusiones de los puntos de referencia
Para el case I
, arrayfun
parece ser el camino a seguir, mientras que para el Case II
, bsxfun
podría ser el arma de elección. Por lo tanto, parece que el tipo de datos con los que está tratando realmente determinará qué enfoque utilizar.
Por lo que sé, no hay una función equivalente para hacer eso en MATLAB, aunque R
tiene rep
que puede hacer eso por ti ... tan celoso.
En cualquier caso, la única forma en que puedo sugerir es ejecutar un bucle for
con repmat
como sugirió. Sin embargo, quizás pueda hacer una arrayfun
si quiere hacer esto como una línea ... bueno, técnicamente es necesario hacer el procesamiento posterior para lograr esto en un solo vector. Como tal, puedes probar esto:
Anew = arrayfun(@(x) repmat(A(x), r(x), 1), 1:numel(A), ''uni'', 0);
Anew = vertcat(Anew{:});
Esto esencialmente hace el bucle for
y la concatenación de los vectores replicados con menos código. Pasamos por cada par de valores en A
y r
y escupimos vectores replicados. Cada uno de ellos estará en una matriz de celdas, por lo que se requiere vertcat
para ponerlo todo en un solo vector.
Obtenemos:
Anew =
0.1000
0.1000
0.1000
0.1000
0.1000
0.2000
0.2000
0.3000
0.3000
0.3000
0.4000
0.4000
0.5000
Tenga en cuenta que otras personas han intentado algo similar a lo que está haciendo en esta publicación: una función similar a la representante de R en Matlab . Esto es esencialmente imitar la forma en que R
hace un rep
, ¡que es lo que quieres hacer!
Alternativa - Uso for
bucles
Debido a la evaluación comparativa de @Divakar, tengo curiosidad por ver cómo la asignación previa de la matriz, luego el uso de un ciclo real for
iterar a través de A
r
y poblarlo mediante la indexación sería un punto de referencia. Como tal, el código equivalente al anterior usando for
bucles e indexación sería:
Anew = zeros(sum(r), 1);
counter = 1;
for idx = 1 : numel(r)
Anew(counter : counter + r(idx) - 1) = A(idx);
counter = counter + r(idx);
end
Necesitaríamos una variable que haga un seguimiento de dónde necesitamos insertar elementos en la matriz, que se almacena en el counter
. Compensamos esto por la cantidad total de elementos para replicar por número, que se almacena en cada valor de r
.
Como tal, este método evita por completo el uso de repmat
y simplemente utiliza la indexación para generar nuestros vectores replicados.
Benchmarking (à la Divakar)
Sobre la base del código de evaluación comparativa de Divakar, en realidad intenté ejecutar todas las pruebas en mi máquina, además del enfoque de ciclo for
. Simplemente utilicé su código de evaluación comparativa con los mismos casos de prueba.
Estos son los resultados de tiempo que obtengo por algoritmo:
Caso # 1 - N = 4000
, max_repeat = 4000
------------------- With arrayfun
Elapsed time is 1.202805 seconds.
------------------- With cumsum
Elapsed time is 1.691591 seconds.
------------------- With bsxfun
Elapsed time is 0.835201 seconds.
------------------- With for loop
Elapsed time is 0.136628 seconds.
Caso # 2 - N = 10000
, max_repeat = 1000
------------------- With arrayfun
Elapsed time is 2.117631 seconds.
------------------- With cumsum
Elapsed time is 1.080247 seconds.
------------------- With bsxfun
Elapsed time is 0.540892 seconds.
------------------- With for loop
Elapsed time is 0.127728 seconds.
En estos casos, cumsum
realidad supera a arrayfun
... que es lo que originalmente esperaba. bsxfun
supera a todos los demás, a excepción del bucle for
. Mi suposición es que con los diferentes tiempos en el arrayfun
entre yo y Divakar, estamos ejecutando nuestro código en diferentes arquitecturas. Actualmente estoy ejecutando mis pruebas con MATLAB R2013a en una MacBook X 10.9.5 MacBook Pro.
Como podemos ver, el ciclo for
es mucho más rápido. Sé con certeza que cuando se trata de operaciones de indexación en un bucle for
, el JIT se activa y le ofrece un mejor rendimiento.