J'ai un long vecteur et j'ai besoin de la diviser en segments selon un seuil. Un segment est des valeurs consécutives sur le seuil. Lorsque les valeurs tombent en dessous du seuil, le segment se termine et le segment suivant commence où les valeurs se croisent à nouveau au-dessus du seuil. Je dois enregistrer les indices de début et de fin de chaque segment.
ci-dessous est une implémentation inefficace. Quel est le moyen le plus rapide et le plus approprié d'écrire cela? C'est assez moche, je dois supposer qu'il y a une implémentation plus propre. P> edit: exécution de toutes les solutions strong> p> Merci pour toutes les réponses, cela a été utile et très instructif. Un petit test de toutes les cinq solutions est inférieur (les quatre fournis plus l'exemple original). Comme vous pouvez le constater, les quatre sont une amélioration énorme sur la solution d'origine, mais la solution de Khashaa est de loin le plus rapide. p>
4 Réponses :
bgoldst <- function() with(rle(test.vec>threshold),t(matrix(c(0L,rep(cumsum(lengths),2L)[-length(lengths)]),2L,byrow=T)+1:0)[values,]); simong <- function() findSegments(test.vec,threshold); set.seed(1); test.vec <- rnorm(1e7,8,10); threshold <- 0; identical(bgoldst(),unname(simong())); ## [1] TRUE system.time({ bgoldst(); }) ## user system elapsed ## 1.344 0.204 1.551 system.time({ simong(); }) ## user system elapsed ## 0.656 0.109 0.762
Voici une autre option, principalement en utilisant quel code>. Les points de début et de fin sont déterminés en trouvant les éléments non consécutifs de la séquence code> clip code>.
> round(test.vec,1)
[1] 20.7 15.7 4.3 -15.1 24.6 9.4 23.2 -4.5 16.9 20.9 13.2 -1.2
[13] 22.6 7.7 6.0 6.6 4.1 21.3 5.3 16.7 11.4 16.7 19.6 16.7
[25] 11.6 7.3 3.7 8.4 -4.5 11.7 -7.1 8.4 -18.5 12.8 22.5 11.0
[37] -3.3 11.1 6.9 -7.9 22.9 -3.7 3.5 -7.1 -5.9 3.5 13.2 20.0
[49] 13.2 23.4 15.9 -5.0 -6.3 10.0 -6.2 4.7 2.1 26.4 5.9 27.3
[61] 14.3 -12.4 28.4 30.9 18.2 11.4 5.7 -4.5 6.2 12.0 10.9 11.1
[73] -2.0 -9.0 -1.4 15.4 19.1 -1.6 -5.4 5.4 7.8 -5.6 15.2 13.8
[85] -18.8 7.1 17.1 9.3 -3.9 22.6 1.7 28.9 -21.3 21.2 8.2 -15.4
[97] 3.2 -10.2 -6.2 14.1
Voici une autre solution que je pense est plus simple. Notez que vous devez utiliser set.sed.sed (10) code>, pas
set.sed , pour définir la graine du générateur de nombres aléatoires.
require(dplyr) # for lead() and lag()
set.seed(10)
test.vec <- rnorm(100, 8, 10)
threshold <- 0
in.segment <- (test.vec > threshold)
start <- which(c(FALSE, in.segment) == TRUE & lag(c(FALSE, in.segment) == FALSE)) - 1
end <- which(c(in.segment, FALSE) == TRUE & lead(c(in.segment, FALSE) == FALSE))
segments <- data.frame(start, end)
head(segments)
## start end
## 1 1 2
## 2 4 6
## 3 8 8
## 4 10 16
## 5 18 21
## 6 23 23
J'aime pour les boucles code> pour la traduction sur
RCPP code> est simple.
Rcpp::cppFunction('DataFrame findSegment(NumericVector x, double threshold) {
x.push_back(-1);
int n = x.size(), startind, endind;
std::vector<int> startinds, endinds;
bool insegment = false;
for(int i=0; i<n; i++){
if(!insegment){
if(x[i] > threshold){
startind = i + 1;
insegment = true; }
}else{
if(x[i] < threshold){
endind = i;
insegment = false;
startinds.push_back(startind);
endinds.push_back(endind);
}
}
}
return DataFrame::create(_["start"]= startinds, _["end"]= endinds);
}')
set.seed(1); test.vec <- rnorm(1e7,8,10); threshold <- 0;
system.time(findSegment(test.vec, threshold))
# user system elapsed
# 0.045 0.000 0.045
# @SimonG's solution
system.time(findSegments(test.vec, threshold))
# user system elapsed
# 0.533 0.012 0.548