finding "almost" duplicates indices in a data table and calculate the delta - r
i have a smallish (2k) data set that contains questionnaire answers filled out by students there were sampled twice a year. not all the students that were present for the first wave were there for the second wave and vice versa. for each student, a unique id was created that consisted of the school code, the class code, the student number and the wave as a decimal point. for example 100612.1 is a student from school 10, grade 6, 12 on the names list and this was the first wave. the idea behind the decimal point was a way to identify the same student again in the data set (the only value which differs less than abs(1) from a given id is the same student on the other wave).at least that was the idea.
i was thinking of a script that would do the following:
- find the rows who's unique id is less than abs(1) from one another
- for those rows, generate a new row (in a new table) that consists of the student id and the delta of the measured variables( i.e value in the wave 2 - value in wave 1).
i a new to R but i have a tiny bit of background in other OOP. i thought about creating a for loop that runs from 1 to length(df) and just looks for it's "brother". my gut feeling tells me that this not the way things are done in R. any ideas?
all i need is a quick way of sifting through the data looking for the second wave row. i think the rest should be straight forward from there.
thank you for helping
PS. since this is my first post here i apologize beforehand for any wrongdoings in this post... :)
The question alludes to data.table, so here is a way to adapt #jed's answer using that package.
ids <- c(100612.1,100612.2,100613.1,100613.2,110714.1,201802.2)
answers <- c(5,4,3,4,1,0)
Example data as before, now instead of data.frame and tapply you can do this:
library(data.table)
surveyDT <- data.table(ids, answers)
surveyDT[, `:=` (child = substr(ids, 1, 6), wave = substr(ids, 8, 8))] # split ID's
# note multiple assign-by-reference := syntax above
setkey(surveyDT, child, wave) # order data
# calculate delta on keyed data, grouping by child
surveyDT[, delta := diff(answers), by = child]
unique(surveyDT[, delta, by = child]) # list results
child delta
1: 100612 -1
2: 100613 1
3: 110714 NA
4: 201802 NA
To remove rows with NA values for delta:
unique(surveyDT[, .SD[(!is.na(delta))], by = child])
child ids answers wave delta
1: 100612 100612.1 5 1 -1
2: 100613 100613.1 3 1 1
Use .SDcols to output only specific columns (in addition to the by columns), for example,
unique(surveyDT[, .SD[(!is.na(delta))], by = child, .SDcols = 'delta'])
child delta
1: 100612 -1
2: 100613 1
It took me some time to get acquainted with data.table syntax, but now I find it more intuitive, and it's fast for big data.
There are two ways that come to mind. The easiest is to use the function floor(), which returns the integer For example:
floor(100612.1)
#[1] 100612
floor(9.9)
#[1] 9
Alternatively, you could write a fairly simple regex expression to get rid of the decimal place too. Then you can use unique() to find the rows that are or are not duplicated entries.
Lets make some fake data so we can see our problem easily:
ids <- c(100612.1,100612.2,100613.1,100613.2,110714.1,201802.2)
answers <- c(5,4,3,4,1,0)
survey <- data.frame(ids,answers)
Now lets split our ids into two different columns:
survey$child_id <- substr(survey$ids,1,6)
survey$wave_id <- substr(survey$ids,8,8)
Then we'll order by child and wave, and compute differences based on child:
survey[order(survey$child_id, survey$wave_id),]
survey$delta <- unlist(tapply(survey$answers, survey$child_id, function(x) c(NA,diff(x))))
Output:
ids answers child_id wave_id delta
1 100612.1 5 100612 1 NA
2 100612.2 4 100612 2 -1
3 100613.1 3 100613 1 NA
4 100613.2 4 100613 2 1
5 110714.1 1 110714 1 NA
6 201802.2 0 201802 2 NA
Related
R: Dropping variables using number of observations
I have a large dataset, and I'm trying to drop some of my variables based on how many observations each has. For instance, I would like to drop any variable in my dataframe where n < 3 (total observations for that variable is less than 3). Since R can count observations for each variable using describe, can't I use that number to subset the data instead of having to type in each variable name each time I pull in a new version (each version has different variables that will have low n's and there are over 40 variables). Thanks so much for your help! For instance, my data looks like this: ID Runaway Aggressive Emergency Hospitalization Injury 1 3 NA 4 1 NA 2 NA NA 2 1 NA 3 4 NA 6 2 3 4 1 NA 1 1 NA I want to be able to drop "Aggressive" and "Injury" based on their n's being 0 and 1 respectively. However, instead of telling R to drop them by variable name, it would be much more convenient if it was possible to tell R to drop any variable where n < 3 (or whatever number I choose) as I'll be using this code for multiple versions of this dataset. I have tried using column numbers (which is better than writing them out) but it's still pretty tedious when I have to describe() the data, figure out which variables have low n's, and then drop 28 variables or subset() around them. This works but it's cumbersome... UIRCorrelation <- UIRKidUnique61[c(28, 30, 32, 34:38, 42, 54:74)] For some reason, my example looks different when I'm editing versus when I save so I also included an image of it. Sorry. This is the first time I've ever used stack overflow to ask a question. I actually spent a lot of time googling this but couldn't find an answer relating to n. This line did not work: DF[, sapply(DF, function(col) length(na.omit(col))) > 4]
DF being your dataframe DF[, sapply(DF, function(col) length(na.omit(col))) > 4]
This function did the trick: valid <- function(x) {sum(!is.na(x))} N <- apply(UIRCorrelation,2,valid) UIRCorrelation2 <- UIRCorrelation[N > 3]
Complex data calculation for consecutive zeros at row level in R (lag v/s lead)
I have a complex calculation that needs to be done. It is basically at a row level, and i am not sure how to tackle the same. If you can help me with the approach or any functions, that would be really great. I will break my problem into two sub-problems for simplicity. Below is how my data looks like Group,Date,Month,Sales,lag7,lag6,lag5,lag4,lag3,lag2,lag1,lag0(reference),lead1,lead2,lead3,lead4,lead5,lead6,lead7 Group1,42005,1,2503,1,1,0,0,0,0,0,0,0,0,0,0,1,0,1 Group1,42036,2,3734,1,1,1,1,1,0,0,0,0,1,1,0,0,0,0 Group1,42064,3,6631,1,0,0,1,0,0,0,0,0,0,1,1,1,1,0 Group1,42095,4,8606,0,1,0,1,1,0,1,0,1,1,1,0,0,0,0 Group1,42125,5,1889,0,1,1,0,1,0,0,0,0,0,0,0,1,1,0 Group1,42156,6,4819,0,1,0,0,0,1,0,0,1,0,1,1,1,1,0 Group1,42186,7,5120,0,0,1,1,1,1,1,0,0,1,1,0,1,1,0 I have data for each Group at Monthly Level. I would like to capture the below two things. 1. The count of consecutive zeros for each row to-and-fro from lag0(reference) The highlighted yellow are the cases, that are consecutive with lag0(reference) to a certain point, that it reaches first 1. I want to capture the count of zero's at row level, along with the corresponding Sales value. Below is the output i am looking for the part1. Output: Month,Sales,Count 1,2503,9 2,3734,3 3,6631,5 4,8606,0 5,1889,6 6,4819,1 7,5120,1 2. Identify the consecutive rows(row:1,2 and 3 & similarly row:5,6) where overlap of any lag or lead happens for any 0 within the lag0(reference range), and capture their Sales and Month value. For example, for row 1,2 and 3, the overlap happens at atleast lag:3,2,1 & lead: 1,2, this needs to be captured and tagged as case1 (or 1). Similarly, for row 5 and 6 atleast lag1 is overlapping, hence this needs to be captured, and tagged as Case2(or 2), along with Sales and Month value. Now, row 7 is not overlapping with the previous or later consecutive row,hence it will not be captured. Below is the result i am looking for part2. Month,Sales,Case 1,2503,1 2,3734,1 3,6631,1 5,1889,2 6,4819,2 I want to run this for multiple groups, hence i will either incorporate dplyr or loop to get the result. Currently, i am simply looking for the approach. Not sure how to solve this problem. First time i am looking to capture things at row level in R. I am not looking for any solution. Simply looking for a first step to counter this problem. Would appreciate any leads.
An option using rle for the 1st part of the calculation can be as: df$count <- apply(df[,-c(1:4)],1,function(x){ first <- rle(x[1:7]) second <- rle(x[9:15]) count <- 0 if(first$values[length(first$values)] == 0){ count = first$lengths[length(first$values)] } if(second$values[1] == 0){ count = count+second$lengths[1] } count }) df[,c("Month", "Sales", "count")] # Month Sales count # 1 1 2503 9 # 2 2 3734 3 # 3 3 6631 5 # 4 4 8606 0 # 5 5 1889 6 # 6 6 4819 1 # 7 7 5120 1 Data: df <- read.table(text = "Group,Date,Month,Sales,lag7,lag6,lag5,lag4,lag3,lag2,lag1,lag0(reference),lead1,lead2,lead3,lead4,lead5,lead6,lead7 Group1,42005,1,2503,1,1,0,0,0,0,0,0,0,0,0,0,1,0,1 Group1,42036,2,3734,1,1,1,1,1,0,0,0,0,1,1,0,0,0,0 Group1,42064,3,6631,1,0,0,1,0,0,0,0,0,0,1,1,1,1,0 Group1,42095,4,8606,0,1,0,1,1,0,1,0,1,1,1,0,0,0,0 Group1,42125,5,1889,0,1,1,0,1,0,0,0,0,0,0,0,1,1,0 Group1,42156,6,4819,0,1,0,0,0,1,0,0,1,0,1,1,1,1,0 Group1,42186,7,5120,0,0,1,1,1,1,1,0,0,1,1,0,1,1,0", header = TRUE, stringsAsFactors = FALSE, sep = ",")
R counting a field in data.table [duplicate]
This question already has answers here: Counting unique / distinct values by group in a data frame (12 answers) Closed 4 years ago. I have a data table which could be reduced to this: set.seed(1); dt<-data.table(form=c(1,1,1,2,3,3,3,4,4,5), mx=c("a","b","c","d","e","f","g","e","g","b"), vr=runif(10,100,200), usr=c("l","l","l","m","o","o","o","l","l","m"), type=c("A","A","A","C","C","C","C","C","C","A")) I can generate a table with: dt[, list(n.form=length(unique(form)),n.mx=length(unique(mx)),tot.vr=sum(vr)), by=usr] What I haven't been able to to is to count the number of formulas of type A (each row is an observation, the form is the formula number). I've tried: dt[, list(n.form=length(unique(form)),n.mx=length(unique(mx)),tot.vr=sum(vr),n.A=sum(type=="A"), by=usr] and also: dt[, list(n.form=length(unique(form)),n.mx=length(unique(mx)),tot.vr=sum(vr),n.A=length(unique(type=="A"))), by=usr] but none of those takes into account the fact that the number of "A" found needs to be related to the unique formula (form) number. What I'd like to have as a result is: usr n.form n.mx tot.vr n.A 1: l 2 5 750.0398 1 2: m 2 2 296.9994 1 3: o 1 3 504.4747 0 but I can't find a way to achieve it. Any light shed is much appreciated. Thanks, ======= EDIT TO ADD ======== I want to know how many of the formulas (unique numbers in dt$form) are of type "A" (so I can calculate a proportion out of total formulas). The direct number (sum) is the total number of observations of type A, while the existence (any) gives me if there was at least one formula of type "A", but not the number of formulas of that type (which is what I want). Please notice that any given formula will always be either of type "A" or "C" (not mixed types in one formula)
In the devel version of data.table, you can use uniqueN instead of length(unique(.., library(data.table)#v1.9.5+ dt[,list(n.form=uniqueN(form), n.mx=uniqueN(mx),tot.vr=sum(vr), n.A=uniqueN(form[type=='A'])) , by = usr] # usr n.form n.mx tot.vr n.A #1: l 2 5 750.0398 1 #2: m 2 2 296.9994 1 #3: o 1 3 504.4747 0
Extract the first, second and last row that meets a criterion
I would like to know how to extract the last row that follow a criterion. I have seen the solution for getting the first one by the function "duplicate" in the next link How do I select the first row in an R data frame that meets certain criteria?. However is it possible to get the second or last row that meet a criterion? I would like to make a loop for each Class (here I only put two) and select the first, second and last row that meet the criterion Weight >= 10. And if there is no row that meets the criterion to get a NA. Finally I want to store the three values (first, second, and last row) in a list containing the values for each class. Class Weight 1 A 20 2 A 15 3 B 10 4 B 23 5 A 11 6 B 12 7 B 11 8 A 25 9 A 7 10 B 3
Data table can help with this. This is an edit off of Davids comment to move it into the answers as his approach is the correct way to do this. library(data.table) DT <- as.data.table(db) DT[Weight >= 10][, .SD[c(1, 2, .N)], by = Class] As as faster alternative also from David look at indx <- DT[Weight >= 10][, .I[c(1, 2, .N)], by = Class]$V1 ; DT[indx] Which creates the wanted index using .I and then subsets DT based on those rows.
create new dataframe based on 2 columns
I have a large dataset "totaldata" containing multiple rows relating to each animal. Some of them are LactationNo 1 readings, and others are LactationNo 2 readings. I want to extract all animals that have readings from both LactationNo 1 and LactationNo 2 and store them in another dataframe "lactboth" There are 16 other columns of variables of varying types in each row that I need to preserve in the new dataframe. I have tried merge, aggregate and %in%, but perhaps I'm using them incorrectly eg. (lactboth <- totaldata[totaldata$LactationNo %in% c(1,2), ]) Animal Id is column 1, and lactationno is column 2. I can't figure out how to select only those AnimalId with LactationNo=1&2 Have also tried lactboth <- totaldata[ which(totaldata$LactationNo==1 & totaldata$LactationNo ==2), ] I feel like this should be simple, but couldn't find an example to follow quite the same. Help appreciated!!
If I understand your question correctly, then your dataset looks something like this: AnimalId LactationNo 1 A 1 2 B 2 3 E 2 4 A 2 5 E 2 and you'd like to select animals that happen to have both lactation numbers 1 & 2 (like A in this particular example). If that's the case, then you can simply use merge: lactboth <- merge(totaldata[totaldata$LactationNo == 1,], totaldata[totaldata$LactationNo == 2,], by.x="AnimalId", by.y="AnimalId")[,"AnimalId"]