2015-04-02 5 views
0

Я работаю с данными GPS и пытаюсь выяснить, как усреднить 11-15-е исправления для широты и долготы. Я видел решения в похожих вопросах, как усреднять каждые n строк. Проблема в том, что иногда спутники взрываются, а исправления останавливаются на 13 или 14. Таким образом, в этих случаях я хочу только усреднять 3 или 4 значения вместо 5. Поэтому я ищу средние значения для широты и долготы, начиная с где число в ряду равно 11, пока число в ряду не упадет снова (или пока оно увеличивается? Мне нужно, чтобы он включал последний набор, который снова не будет возвращаться к низкому числу). Я начал с удаления всех строк, где число в серии НЕ в моих желаемых диапазонах 11-15. Так, для примера фиктивного набора данных, это оставляет меня:В R, среднее значение строки до тех пор, пока не наступит определенное условие, затем перезапустите, с выходом в новом столбце

 Date  Time  Long  Lat  NoInSeries 
12 17/11/2014 22:09:17 115.9508 -31.82850 11 
13 17/11/2014 22:09:18 115.9508 -31.82846 12 
14 17/11/2014 22:09:19 115.9513 -31.82864 13 
15 17/11/2014 22:09:21 115.9511 -31.82863 14 
26 18/11/2014 00:07:14 115.9509 -31.82829 11 
27 18/11/2014 00:07:15 115.9509 -31.82829 12 
28 18/11/2014 00:07:16 115.9509 -31.82830 13 
29 18/11/2014 00:07:17 115.9509 -31.82830 14 
30 18/11/2014 00:07:18 115.9509 -31.82831 15 
56 18/11/2014 10:00:24 115.9513 -31.82670 11 
57 18/11/2014 10:00:25 115.9514 -31.82670 12 
58 18/11/2014 10:00:26 115.9514 -31.82669 13 
59 18/11/2014 10:00:27 115.9514 -31.82668 14 
60 18/11/2014 10:00:28 115.9514 -31.82668 15 

Мой желаемый результат будет что-то вроде этого, с первым один в среднем 4 (11-14), а следующие два в среднем 5 (11- 15):

 Date  Time  Long  Lat  NoInSeries AvgLong  Avg Lat 
12 17/11/2014 22:09:17 115.9508 -31.82850 11  115.9510 -31.82856 
13 17/11/2014 22:09:18 115.9508 -31.82846 12   NA   NA 
14 17/11/2014 22:09:19 115.9513 -31.82864 13   NA   NA 
15 17/11/2014 22:09:21 115.9511 -31.82863 14   NA   NA 
26 18/11/2014 00:07:14 115.9509 -31.82829 11  115.9509 -31.82830 
27 18/11/2014 00:07:15 115.9509 -31.82829 12   NA   NA 
28 18/11/2014 00:07:16 115.9509 -31.82830 13   NA   NA 
29 18/11/2014 00:07:17 115.9509 -31.82830 14   NA   NA 
30 18/11/2014 00:07:18 115.9509 -31.82831 15   NA   NA 
56 18/11/2014 10:00:24 115.9513 -31.82670 11  115.9514 -31.82669 
57 18/11/2014 10:00:25 115.9514 -31.82670 12   NA   NA 
58 18/11/2014 10:00:26 115.9514 -31.82669 13   NA   NA 
59 18/11/2014 10:00:27 115.9514 -31.82668 14   NA   NA 
60 18/11/2014 10:00:28 115.9514 -31.82668 15   NA   NA 

Я бы тогда пройти и удалить все строки, в которых AvgLong == NA, так что мой окончательный вывод будет только иметь все строки, где число в серии = 11 с средними.

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

Например:

c(tapply(x, (row(x)-1)%/%5, mean)) 

Или:

idx <- ceiling(seq_len(nrow(dd))/5) 
# do colMeans on all columns except last one. 
res <- lapply(split(dd[-(ncol(dd))], idx), colMeans, na.rm = TRUE) 
# assign first value of "datetime" in each 5-er group as names to list 
names(res) <- dd$datetime[seq(1, nrow(df), by=5)] 
# bind them to give a matrix 
res <- do.call(rbind, res) 

Кроме того, ответы на которые я видел, как правило, тогда выходные средние значения как новый кадр данных ... В конце концов, я тоже хочу иметь это усреднение при условии: если расписание - это «Multifix», я хочу, чтобы средний показатель был до 11, но до 15, тогда как если график «Непрерывный», я хочу усреднить с 181 до тех пор, пока все не пройдут. .). Что-то вроде этого:

if(import.list$Schedule=='Multifix'){ 
...code to average Long and Lat for Number in Series from 11 up to however high it goes (up to 15)... 
} else { 
...code to average Long and Lat for Number in Series from 241 up to however high it goes... 
} 

Или, возможно, у меня есть, если другое заявление, чтобы определить переменную, а затем использовать эту переменную в функции, чтобы сделать усреднение?

... но я полагаю, что это условие может усложнить ситуацию, если на выходе создается новый dataframe, поэтому я стремился просто добавлять значения к новым столбцам «AvgLong» и «AvgLat». Спасибо за любую помощь!

ответ

1

#dput показывает данные, которые я работал с вашим вопросом.

dput(df1) 
structure(list(ID = c(12L, 13L, 14L, 15L, 26L, 27L, 28L, 29L, 
30L, 56L, 57L, 58L, 59L, 60L), Date = c("17/11/2014", "17/11/2014", 
"17/11/2014", "17/11/2014", "18/11/2014", "18/11/2014", "18/11/2014", 
"18/11/2014", "18/11/2014", "18/11/2014", "18/11/2014", "18/11/2014", 
"18/11/2014", "18/11/2014"), Time = c("22:09:17", "22:09:18", 
"22:09:19", "22:09:21", "00:07:14", "00:07:15", "00:07:16", "00:07:17", 
"00:07:18", "10:00:24", "10:00:25", "10:00:26", "10:00:27", "10:00:28" 
), Long = c(115.9508, 115.9508, 115.9513, 115.9511, 115.9509, 
115.9509, 115.9509, 115.9509, 115.9509, 115.9513, 115.9514, 115.9514, 
115.9514, 115.9514), Lat = c(-31.8285, -31.82846, -31.82864, 
-31.82863, -31.82829, -31.82829, -31.8283, -31.8283, -31.82831, 
-31.8267, -31.8267, -31.82669, -31.82668, -31.82668), NoInSeries = c(11L, 
12L, 13L, 14L, 11L, 12L, 13L, 14L, 15L, 11L, 12L, 13L, 14L, 15L 
)), .Names = c("ID", "Date", "Time", "Long", "Lat", "NoInSeries" 
), class = "data.frame", row.names = c(NA, -14L)) 

# get.counter получает индекс строки, когда значение столбца начинает уменьшаться в противоположность возрастанию.

get.counter <- function(x){ 
    a1 = x 
    counter = 0 
    a2 = c() 
    for(i in 1:length(a1)){ 
    if(i < length(a1)){ 
     if(a1[i+1] > a1[i]){ 
     counter = counter + 1 
     }else{ 
     counter = counter + 1 
     a2 = c(a2, counter) 
     counter = 0 
     } 
    }else{ 
     counter = counter + 1 
     a2 = c(a2, counter) 
    } 
    } 
    return(a2) 
} 

# Функция avg.seg.col выводит кадр данных с сегментированным средним значением столбца. df1 - это входной кадр данных, colvar - это имя столбца (например: Long или Lat), а get_counter - результат функции get.counter.

avg.seg.col <- function(df1, colvar, get_counter){ 

    long <- c() 

    start = 1 

    for(i in cumsum(get_counter)){ 
    end = i 
    b1 = subset(df1, select = colvar)[start:end,] 

    mean_b1 = mean(b1) 

    long = c(long, mean_b1, rep(NA, (length(b1)-1))) 

    start = end+1 
    } 
    return(data.frame(long, stringsAsFactors = FALSE)) 
} 

# читать данные из текстового файла, используя функцию read.table. Вы должны убедиться, что ваш файл существует в текущем рабочем каталоге.Рабочий каталог может быть установлен setwd ("путь текущей рабочей директории")

df1 <- read.table(file = "file1.txt", 
        header = TRUE, 
        sep = "\t", 
        stringsAsFactors = FALSE) 

# применять get.counter функцию с вектором из df1 $ NoInSeries

get_counter <- get.counter(df1$NoInSeries) 

# Применить avg.seg.col функция для длинной колонки

AvgLong <- avg.seg.col(df1, "Long", get_counter) 

# Применить avg.seg.col функции Lat колонки

AvgLat <- avg.seg.col(df1, "Lat", get_counter) 

# Объединение кадров данных от столбца

df2 <- do.call("cbind", list(df1, AvgLong, AvgLat)) 

# Присвоить имена столбцов

colnames(df2) <- c(colnames(df2)[1:(ncol(df2)-2)], "AvgLong", "AvgLat") 

Выход:

 print(df2) 
    ID  Date  Time  Long  Lat NoInSeries AvgLong AvgLat 
1 12 17/11/2014 22:09:17 115.9508 -31.82850   11 115.9510 -31.82856 
2 13 17/11/2014 22:09:18 115.9508 -31.82846   12  NA  NA 
3 14 17/11/2014 22:09:19 115.9513 -31.82864   13  NA  NA 
4 15 17/11/2014 22:09:21 115.9511 -31.82863   14  NA  NA 
5 26 18/11/2014 00:07:14 115.9509 -31.82829   11 115.9509 -31.82830 
6 27 18/11/2014 00:07:15 115.9509 -31.82829   12  NA  NA 
7 28 18/11/2014 00:07:16 115.9509 -31.82830   13  NA  NA 
8 29 18/11/2014 00:07:17 115.9509 -31.82830   14  NA  NA 
9 30 18/11/2014 00:07:18 115.9509 -31.82831   15  NA  NA 
10 56 18/11/2014 10:00:24 115.9513 -31.82670   11 115.9514 -31.82669 
11 57 18/11/2014 10:00:25 115.9514 -31.82670   12  NA  NA 
12 58 18/11/2014 10:00:26 115.9514 -31.82669   13  NA  NA 
13 59 18/11/2014 10:00:27 115.9514 -31.82668   14  NA  NA 
14 60 18/11/2014 10:00:28 115.9514 -31.82668   15  NA  NA 

#after удаление строк с NA, выходной сигнал выглядит, как показано ниже

df2[-(which(df2$AvgLong %in% NA)), ] 
    ID  Date  Time  Long  Lat NoInSeries AvgLong AvgLat 
1 12 17/11/2014 22:09:17 115.9508 -31.82850   11 115.9510 -31.82856 
5 26 18/11/2014 00:07:14 115.9509 -31.82829   11 115.9509 -31.82830 
10 56 18/11/2014 10:00:24 115.9513 -31.82670   11 115.9514 -31.82669 
1

Кажется, что использование aggregate делает большую часть работы:

> aggregate(df1[ ,c("ID", "Long","Lat")], list((df1$ID-1) %/% 5), mean) 
    Group.1 ID  Long  Lat 
1  2 13.5 115.9510 -31.82856 
2  5 28.0 115.9509 -31.82830 
3  11 58.0 115.9514 -31.82669 

Необходимо, чтобы переместить переменную ID по одному, чтобы получить по модулю Divison для доставки групп, которые вы хотели. Если вы хотите иметь что-то выровненный с Origianl данных, то функция ave предназначен для доставки:

> df1$aveLong <- ave(df1$Long, (df1$ID-1) %/% 5, 
      FUN=function(x) c(mean(x), rep(NA, length(x)-1))) 
> df1$aveLLat <- ave(df1$Lat, (df1$ID-1) %/% 5, 
      FUN=function(x) c(mean(x), rep(NA, length(x)-1))) 
> df1 
    ID  Date  Time  Long  Lat NoInSeries aveLong 
1 12 17/11/2014 22:09:17 115.9508 -31.82850   11 115.9510 
2 13 17/11/2014 22:09:18 115.9508 -31.82846   12  NA 
3 14 17/11/2014 22:09:19 115.9513 -31.82864   13  NA 
4 15 17/11/2014 22:09:21 115.9511 -31.82863   14  NA 
5 26 18/11/2014 00:07:14 115.9509 -31.82829   11 115.9509 
6 27 18/11/2014 00:07:15 115.9509 -31.82829   12  NA 
7 28 18/11/2014 00:07:16 115.9509 -31.82830   13  NA 
8 29 18/11/2014 00:07:17 115.9509 -31.82830   14  NA 
9 30 18/11/2014 00:07:18 115.9509 -31.82831   15  NA 
10 56 18/11/2014 10:00:24 115.9513 -31.82670   11 115.9514 
11 57 18/11/2014 10:00:25 115.9514 -31.82670   12  NA 
12 58 18/11/2014 10:00:26 115.9514 -31.82669   13  NA 
13 59 18/11/2014 10:00:27 115.9514 -31.82668   14  NA 
14 60 18/11/2014 10:00:28 115.9514 -31.82668   15  NA 
    aveLLat 
1 -31.82856 
2   NA 
3   NA 
4   NA 
5 -31.82830 
6   NA 
7   NA 
8   NA 
9   NA 
10 -31.82669 
11  NA 
12  NA 
13  NA 
14  NA 
+0

Из любопытства вы могли бы использовать функцию «ave» при подмножестве кадра данных для неравномерных сегментов. Например, первые 5 строк, затем 10 строк, затем 6 строк ... и так далее. Мое решение может выполнять такую ​​задачу. – Sathish

+0

Если INDEX является нерегулярным размером, то функция применяется к соответствующим элементам в первом аргументе. Нет равных размеров. –

3

Вы можете сделать это с помощью cumsum, diff, aggregate и merge

x 
##   Date  Time  Long  Lat NoInSeries SeriesNo 
## 1 17/11/2014 22:09:17 115.9508 -31.82850   11  0 
## 2 17/11/2014 22:09:18 115.9508 -31.82846   12  0 
## 3 17/11/2014 22:09:19 115.9513 -31.82864   13  0 
## 4 17/11/2014 22:09:21 115.9511 -31.82863   14  0 
## 5 18/11/2014 00:07:14 115.9509 -31.82829   11  1 
## 6 18/11/2014 00:07:15 115.9509 -31.82829   12  1 
## 7 18/11/2014 00:07:16 115.9509 -31.82830   13  1 
## 8 18/11/2014 00:07:17 115.9509 -31.82830   14  1 
## 9 18/11/2014 00:07:18 115.9509 -31.82831   15  1 
## 10 18/11/2014 10:00:24 115.9513 -31.82670   11  2 
## 11 18/11/2014 10:00:25 115.9514 -31.82670   12  2 
## 12 18/11/2014 10:00:26 115.9514 -31.82669   13  2 
## 13 18/11/2014 10:00:27 115.9514 -31.82668   14  2 
## 14 18/11/2014 10:00:28 115.9514 -31.82668   15  2 

cumsum(c(0, diff(x$NoInSeries) < 0)) даст вы новый столбец, который увеличивается каждый раз, когда diff из NoInSeries отрицательный.

# Define a new variable which increments after every drop in NoInSeries 
x$SeriesNo <- cumsum(c(0, diff(x$NoInSeries) < 0)) 

Теперь вы aggregate с использованием нового SeriesNo колонки

# Breakdown ... First aggregate Long, Lat by Series No with Function mean 
aggregate(cbind(Long, Lat) ~ SeriesNo, data = x, FUN = mean) 
## SeriesNo  Long  Lat 
## 1  0 115.9510 -31.82856 
## 2  1 115.9509 -31.82830 
## 3  2 115.9514 -31.82669 



# merge it back with original data with only rows where NoInSeries = 11 

# Final Desired Result in one line 
merge(x[x$NoInSeries == 11, c("Date", "Time", "SeriesNo")], aggregate(cbind(Long, 
    Lat) ~ SeriesNo, data = x, FUN = mean)) 
## SeriesNo  Date  Time  Long  Lat 
## 1  0 17/11/2014 22:09:17 115.9510 -31.82856 
## 2  1 18/11/2014 00:07:14 115.9509 -31.82830 
## 3  2 18/11/2014 10:00:24 115.9514 -31.82669 
0

Я прочитал for петли необходимы для итеративных действий, поэтому мне нравится использовать Chinmay по cumsum и diff. У меня недостаточно репутации, чтобы комментировать элегантный ответ @Chinmay Patil, так что вот немного другой подход.

df$group <- 0  #Create a dummy grouping variable 

for(i in 2:length(df$NoInSeries)) {  #Starting on row 2 to the end 
    #Check if the series resets (True = 1, False = 0) 
    check <- df[i-1, "NoInSeries"] > df[i, "NoInSeries"] 
    df[i, "group"] <- df[i-1, "group"] + check #Add check value to previous row 
}  #This yields a number for each series 

require(plyr) 
ddply(df, .(group), summarise, 
    Date= min(Date), Time=min(Time), Long=mean(Long), Lat= mean(Lat)) 

# group  Date  Time  Long  Lat 
#1  0 17/11/2014 22:09:17 115.9510 -31.82856 
#2  1 18/11/2014 00:07:14 115.9509 -31.82830 
#3  2 18/11/2014 10:00:24 115.9514 -31.82669 

Вы можете сообщить широта/долгота в первый раз (min, как указано выше), в последний раз (max), или среднее время (mean). Однако иногда у меня возникают проблемы с ddply, когда у меня есть POSIXct даты/время в фрейме данных.