aggregating data frames by sum - r

Say I have two data frames as follows.
d1 = data.frame(table(c(1,2,2,2,4,5)))
d2 = data.frame(table(c(2,2,2,6)))
Combing the frames with rbind gives the following:
> rbind(d1, d2)
Var1 Freq
1 1 1
2 2 3
3 4 1
4 5 1
5 2 3
6 5 1
But what I would like is to calculate the sum of the Freq values with the same Var1, i.e. get
Var1 Freq
1 1 1
2 2 6
3 4 1
4 5 1
5 6 1
How can I accomplish this?

In addition to aggregate, there is also xtabs which is designed specifically for summing up tables.
xtabs(Freq ~ Var1, data=rbind(d1, d2))

Related

Create function to count occurrences within groups in R

I have a dataset with a unique ID for groups of patients called match_no and i want to count how many patients got sick in two different years by running a loop function to count the occurrences in a large dataset
for (i in db$match_no){(with(db, sum(db$TBHist16 == 1 & db$match_no == i))}
This is my attempt. I need i to cycle through each of the match numbers and count how many TB occurrences there was.
Can anyone correct my formula please.
Example here
df1 <- data.frame(Match_no = c(1, 1,1,1,1,2,2,2,2,2, 3,3,3,3,3, 4,4,4,4,4, 5,5,5,5,5),
var1 = c(1,1,1,0,0,1,1,1,0,0,0,1,1,1,1,1,0,0,0,1,1,1,1,0,1))
I want to count how many 1 values there are in each match number.
Thank you
Some ideas:
Simple summary of all Match_no values:
xtabs(~var1 + Match_no, data = df1)
# Match_no
# var1 1 2 3 4 5
# 0 2 2 1 3 1
# 1 3 3 4 2 4
Same as 1, but with a subset:
xtabs(~ Match_no, data = subset(df1, var1 == 1))
# Match_no
# 1 2 3 4 5
# 3 3 4 2 4
Results in a frame:
aggregate(var1 ~ Match_no, data = subset(df1, var1 == 1), FUN = length)
# Match_no var1
# 1 1 3
# 2 2 3
# 3 3 4
# 4 4 2
# 5 5 4
In base R you can use aggregate and sum:
aggregate(var1 ~ Match_no, data = df1, FUN = sum)
Match_no var1
1 1 3
2 2 3
3 3 4
4 4 2
5 5 4

Add rows to dataframe from another dataframe, based on a vector

I'd like to add rows to a dataframe based on a vector within the dataframe. Here are the dataframes (df2 is the one I'd like to add rows to; df1 is the one I'd like to take the rows from):
ID=c(1:5)
x=c(rep("a",3),rep("b",2))
y=c(rep(0,5))
df1=data.frame(ID,x,y)
df2=df1[2:4,1:2]
df2$y=c(5,2,3)
df1
ID x y
1 1 a 0
2 2 a 0
3 3 a 0
4 4 b 0
5 5 b 0
df2
ID x y
2 2 a 5
3 3 a 2
4 4 b 3
I'd like to add to df2 any rows that aren't in df1, based on the ID vector. so my output dataframe would look like this:
ID x y
1 b 0
5 b 0
2 a 5
3 a 2
4 b 3
Can anyone see a way of doing this neatly, please? I need to do it for a lot of dataframes, all with different numbers of rows. I've tried using merge or rbind but I haven't been able to work out how to do it based on the vector.
Thank you!
A solution with dplyr:
bind_rows(df2,anti_join(df1,df2,by="ID"))
# ID x y
#1 2 a 5
#2 3 a 2
#3 4 b 3
#4 1 a 0
#5 5 b 0
You can do the following:
missingIDs <- which(!df1$ID %in% df2$ID) #check which df1 ID's are not in df2, see function is.element()
df.toadd <- df1[missingIDs,] #define the data frame to add to df2
result <- rbind(df.toadd, df2) #use rbind to add it
result
ID x y
1 1 a 0
5 5 b 0
2 2 a 5
3 3 a 2
4 4 b 3
What about this one-liner?
rbind(df2, df1[!df1$ID %in% df2$ID,])
ID x y
2 2 a 5
3 3 a 2
4 4 b 3
1 1 a 0
5 5 b 0

Create a rolling index of pairs over groups

I need to create (with R) a rolling index of pairs from a data set that includes groups. Consider the following data set:
times <- c(4,3,2)
V1 <- unlist(lapply(times, function(x) seq(1, x)))
df <- data.frame(group = rep(1:length(times), times = times),
V1 = V1,
rolling_index = c(1,1,2,2,3,3,4,5,5))
df
group V1 rolling_index
1 1 1 1
2 1 2 1
3 1 3 2
4 1 4 2
5 2 1 3
6 2 2 3
7 2 3 4
8 3 1 5
9 3 2 5
The data frame I have includes the variables group and V1. Within each group V1 designates a running index (that may or may not start at 1).
I want to create a new indexing variable that looks like rolling_index. This variable groups rows within the same group and consecutive V1 value, thus creating a new rolling index. This new index must be consecutive over groups. If there is an uneven amount of rows within a group (e.g. group 2), then the last, single row gets its own rolling index value.
You can try
library(data.table)
setDT(df)[, gr:=as.numeric(gl(.N, 2, .N)), group][,
rollindex:=cumsum(c(TRUE,abs(diff(gr))>0))][,gr:= NULL]
# group V1 rolling_index rollindex
#1: 1 1 1 1
#2: 1 2 1 1
#3: 1 3 2 2
#4: 1 4 2 2
#5: 2 1 3 3
#6: 2 2 3 3
#7: 2 3 4 4
#8: 3 1 5 5
#9: 3 2 5 5
Or using base R
indx1 <- !duplicated(df$group)
indx2 <- with(df, ave(group, group, FUN=function(x)
gl(length(x), 2, length(x))))
cumsum(c(TRUE,diff(indx2)>0)|indx1)
#[1] 1 1 2 2 3 3 4 5 5
Update
The above methods are based on the 'group' column. Suppose you already have a sequence column ('V1') by group as showed in the example, creation of rolling index is easier
cumsum(!!df$V1 %%2)
#[1] 1 1 2 2 3 3 4 5 5
As mentioned in the post, if the 'V1' column do not start at '1' for some groups, we can get the sequence from the 'group' and then do the cumsum as above
cumsum(!!with(df, ave(seq_along(group), group, FUN=seq_along))%%2)
#[1] 1 1 2 2 3 3 4 5 5
There is probably a simpler way but you can do:
rep_each <- unlist(mapply(function(q,r) {c(rep(2, q),rep(1, r))},
q=table(df$group)%/%2,
r=table(df$group)%%2))
df$rolling_index <- inverse.rle(x=list(lengths=rep_each, values=seq(rep_each)))
df$rolling_index
#[1] 1 1 2 2 3 3 4 5 5

aggregate dataframe subsets in R

I have the dataframe ds
CountyID ZipCode Value1 Value2 Value3 ... Value25
1 1 0 etc etc etc
2 1 3
3 1 0
4 1 1
5 2 2
6 3 3
7 4 7
8 4 2
9 5 1
10 6 0
and would like to aggregate based on ds$ZipCode and set ds$CountyID equal to the primary county based on the highest ds$Value1. For the above example, it would look like this:
CountyID ZipCode Value1 Value2 Value3 ... Value25
2 1 4 etc etc etc
5 2 2
6 3 3
7 4 9
9 5 1
10 6 0
All the ValueX columns are the sum of that column grouped by ZipCode.
I've tried a bunch of different strategies over the last couple days, but none of them work. The best I've come up with is
#initialize the dataframe
ds_temp = data.frame()
#loop through each subset based on unique zipcodes
for (zip in unique(ds$ZipCode) {
sub <- subset(ds, ds$ZipCode == zip)
len <- length(sub)
maxIndex <- which.max(sub$Value1)
#do the aggregation
row <- aggregate(sub[3:27], FUN=sum, by=list(
CountyID = rep(sub$CountyID[maxIndex], len),
ZipCode = sub$ZipCode))
rbind(ds_temp, row)
}
ds <- ds_temp
I haven't been able to test this on the real data, but with dummy datasets (such as the one above), I keep getting the error "arguments must have the same length). I've messed around with rep() and fixed vectors (eg c(1,2,3,4)) but no matter what I do, the error persists. I also occasionally get an error to the effect of
cannot subset data of type 'closure'.
Any ideas? I've also tried messing around with data.frame(), ddply(), data.table(), dcast(), etc.
You can try this:
data.frame(aggregate(df[,3:27], by=list(df$ZipCode), sum),
CountyID = unlist(lapply(split(df, df$ZipCode),
function(x) x$CountyID[which.max(x$Value1)])))
Fully reproducible sample data:
df<-read.table(text="
CountyID ZipCode Value1
1 1 0
2 1 3
3 1 0
4 1 1
5 2 2
6 3 3
7 4 7
8 4 2
9 5 1
10 6 0", header=TRUE)
data.frame(aggregate(df[,3], by=list(df$ZipCode), sum),
CountyID = unlist(lapply(split(df, df$ZipCode),
function(x) x$CountyID[which.max(x$Value1)])))
# Group.1 x CountyID
#1 1 4 2
#2 2 2 5
#3 3 3 6
#4 4 9 7
#5 5 1 9
#6 6 0 10
In response to your comment on Frank's answer, you can preserve the column names by using the formula method in aggregate. Using Franks's data df, this would be
> cbind(aggregate(Value1 ~ ZipCode, df, sum),
CountyID = sapply(split(df, df$ZipCode), function(x) {
with(x, CountyID[Value1 == max(Value1)]) }))
# ZipCode Value1 CountyID
# 1 1 4 2
# 2 2 2 5
# 3 3 3 6
# 4 4 9 7
# 5 5 1 9
# 6 6 0 10

Select max or equal value from several columns in a data frame

I'm trying to select the column with the highest value for each row in a data.frame. So for instance, the data is set up as such.
> df <- data.frame(one = c(0:6), two = c(6:0))
> df
one two
1 0 6
2 1 5
3 2 4
4 3 3
5 4 2
6 5 1
7 6 0
Then I'd like to set another column based on those rows. The data frame would look like this.
> df
one two rank
1 0 6 2
2 1 5 2
3 2 4 2
4 3 3 3
5 4 2 1
6 5 1 1
7 6 0 1
I imagine there is some sort of way that I can use plyr or sapply here but it's eluding me at the moment.
There might be a more efficient solution, but
ranks <- apply(df, 1, which.max)
ranks[which(df[, 1] == df[, 2])] <- 3
edit: properly spaced!

Resources