2016-05-11 14 views
13

Angenommen ich diesen Datenrahmen haben:Werte in einem Datenrahmen in R ausfüllen?

times vals 
1  1 2 
2  3 4 
3  7 6 

mit

foo <- data.frame(times=c(1,3,7), vals=c(2,4,6)) 

einrichten und ich möchte diese:

times vals 
1  1 2 
2  2 2 
3  3 4 
4  4 4 
5  5 4 
6  6 4 
7  7 6 

Das heißt, ich in allen Zeiten füllen wollen von 1 bis 7, und füllen Sie die Werte von der letzten Zeit, die nicht größer als die angegebene Zeit ist.

Ich habe einen Code, um es mit dplyr zu tun, aber es ist hässlich. Vorschläge für bessere?

library(dplyr) 

foo <- merge(foo, data.frame(times=1:max(foo$times)), all.y=TRUE) 
foo2 <- merge(foo, foo, by=c(), suffixes=c('', '.1')) 

foo2 <- foo2 %>% filter(is.na(vals) & !is.na(vals.1) & times.1 <= times) %>% 
    group_by(times) %>% arrange(-times.1) %>% mutate(rn = row_number()) %>% 
    filter(rn == 1) %>% 
    mutate(vals = vals.1, 
     rn = NULL, 
     vals.1 = NULL, 
     times.1 = NULL) 

foo <- merge(foo, foo2, by=c('times'), all.x=TRUE, suffixes=c('', '.2')) 
foo <- mutate(foo, 
       vals = ifelse(is.na(vals), vals.2, vals), 
       vals.2 = NULL) 
+0

'data.frame (zeit = 1: 7, vals = foo $ vals [findInterval (1: 7, foo $ mal)])' wird für NAs funktionieren, da @eddi es für ein so wichtiges Thema gehalten hat – rawr

Antwort

5

A dplyr und tidyr Option:

library(dplyr) 
library(tidyr) 

foo %>% 
right_join(data_frame(times = min(foo$times):max(foo$times))) %>% 
fill(vals) 
# Joining by: "times" 
# times vals 
# 1  1 2 
# 2  2 2 
# 3  3 4 
# 4  4 4 
# 5  5 4 
# 6  6 4 
# 7  7 6 
+1

dies wird falsche Ergebnisse geben, wenn 'foo $ vals' hat' NA's mit – eddi

+0

zu beginnen Nicht inkorrekt, nur kein Rolling Join. Du kannst das tun, wenn du willst: 'if (any (is.na (foo $ vals))) {foo%>% slice (was (is.na (vals)) :(was (is.na (vals)) + 1))%>% right_join (data_frame (mal = seq (. [Seq (1, now (.), Durch = 2), 'mal'],. [Ifelse (nrow (.)> 1, seq (2, now (.), By = 2), 1), 'mal'])))%>% bind_rows (anti_join (foo%>% right_join (daten_rahmen (mal = min (foo $ mal): max (foo $ mal)))%>% füllen (vals),., by = 'mal'))%>% ordnen (mal)} else {foo%>% right_join (data_frame (mal = min (foo $ mal): max (foo $ mal)))%>% fill (vals)}, obwohl es wahrscheinlich einen eleganteren Weg gibt. – alistaire

+0

Von OP: * "... und füllen Sie die Werte von der spätesten Zeit, die nicht größer als die angegebene Zeit ist" * - das ist im Grunde die Lehrbuchdefinition eines rollenden Joins. Ich bin mir nicht sicher, wie Sie das interpretieren könnten, um NAs mit früheren Nicht-NA-Werten zu füllen. – eddi

10

Dies ist ein Standard Roll Problem kommen:

library(data.table) 

setDT(foo)[.(1:7), on = 'times', roll = T] 
# times vals 
#1:  1 2 
#2:  2 2 
#3:  3 4 
#4:  4 4 
#5:  5 4 
#6:  6 4 
#7:  7 6 

Die oben ist für Entwick-Version (1.9.7+), die intelligentere über Spaltenanpassung ist während beitritt. Für 1.9.6 müssen Sie noch Spaltennamen für die innere Tabelle angeben:

setDT(foo)[.(times = 1:7), on = 'times', roll = T] 
+0

Ich weiß es zu schätzen Antworten und Ihre Kommentare zu anderen Antworten zu ihren Einschränkungen in Bezug auf NA. Nur FYI, ich mag data.table nicht (ich finde die Syntax schwer zu lesen), also lehne ich mich Lösungen aus "dem Hadley-Vers" zu (Pakete von Hadley Wickham, wie tidyr, dplyr, ..), sogar mit ihre Grenzen. – dfrankow

+0

@dfrankow sicher, np. Fwiw, hier ist eine sehr einfache Möglichkeit, "data.table" -Syntax zu lesen, die 90% der Syntax verstehen lässt. 'd [i, j, by = b]' wird gelesen als "take' d', wende 'i' an, dann berechne' j' mit 'b'". – eddi

4

Dies ist etwas länger und ausführliche Basis R Lösung:

# calculate the number of repetitions needed for vals variable 
reps <- c(with(foo, times[2:length(times)]-times[1:length(times)-1]), 1) 

# get result 
fooDoneIt <- data.frame(times = min(foo$times):max(foo$times), 
       vals = rep(foo$vals, reps)) 
6

Mit approx:

data.frame(times = 1:7, 
      vals = unlist(approx(foo, xout = 1:7, method = "constant", f = 0)[2], use.names = F)) 

    times vals 
1  1 2 
2  2 2 
3  3 4 
4  4 4 
5  5 4 
6  6 4 
7  7 6 
+1

Schön, das wusste ich nicht! Sie könnten auch einfach auf data.frame zugreifen und die Namen ändern: 'setNames (data.frame (approx (foo, xout = 1: 7, method =" konstant ", f = 0)), name (foo))' – alistaire

+0

das leidet unter dem gleichen Problem wie die Lösung von Alistaire - wird scheitern, wenn es NAs gibt, die mit – eddi

+0

anfangen, ändern 'foo [2, 'vals'] = NA' und run it – eddi

Verwandte Themen