I have an 30 * 9 data frame filled with integers 1-9. Each integer can feature multiple times in a column, or none at all.
I basically wanted to calculate the number of times a number appears, in order to generate a column of 9 rows (of counts) for each element of the original data frame, to end up with a 9 * 9 data frame of counts. I also wanted to have a 0 placed where a number does not appear in a particular column.
So far I tried multiple approaches with for loops, tapply, functions etc. But I cannot seem to end up with a result which can be stored directly into a new data frame in a loop.
for (i in seq_along(columnHeaderQuosureList)) {
original_data_frame %>%
group_by(!! columnHeaderQuosureList[[i]]) %>% # Unquote with !!
count(!! columnHeaderQuosureList[[i]]) %>%
print()
}
This works and prints each count for each column. I tried replacing print() with return() and then trying to cbind the returned output with the result_data_frame.
Unfortunately I am getting nowhere, and I do not think my approach is feasible.
Does anyone have any better ideas please?
The function to count instances of unique values in R is table.
# simulated data
df <- as.data.frame(matrix(sample(1:9, 30*9, TRUE), ncol=9))
# use stack to turn column name into a factor column (long format)
table(stack(df))
ind
values V1 V2 V3 V4 V5 V6 V7 V8 V9
1 4 0 1 2 6 3 6 3 2
2 2 2 5 4 4 4 3 3 2
3 4 4 3 7 2 7 1 4 2
4 1 5 1 3 3 5 6 3 5
5 3 4 4 4 4 2 2 3 6
6 7 5 3 2 3 0 1 3 3
7 4 5 3 3 2 1 3 3 1
8 3 2 3 3 5 2 6 4 3
9 2 3 7 2 1 6 2 4 6
Edit: forgot the tricky bit. The output of table is a table object, which looks like a matrix but gets turned into a long format if you try to do as.data.frame. To turn your result into a 9x9 df, use
as.data.frame.matrix(table(stack(df)))
Caveat: if for some reason one of the 9 digits doesn't appear anywhere in the original df, then that row will be skipped (instead of being filled with 0s).
This is very similar to the behaviour of tabulate(), so you can do:
#Create example data
df <- as.data.frame(matrix(sample(1:9, 30*9, TRUE), ncol=9))
#Counts of digits 1-9
as.data.frame(sapply(df, tabulate, nbins=9))
Here's a tidyverse solution that is robust with respect to the numbert of columns and the number of distinct values they contain. I avoid recourse to loops andnon-standard evaluation by making the data tidy by pivoting.
First, create some test data
library(tidyverse)
# For reproducibility
set.seed(123)
d <- tibble(
c1=floor(runif(30, 1, 10)),
c2=floor(runif(30, 1, 10)),
c3=floor(runif(30, 1, 10)),
c4=floor(runif(30, 1, 10)),
c5=floor(runif(30, 1, 10)),
c6=floor(runif(30, 1, 10)),
c7=floor(runif(30, 1, 10)),
c8=floor(runif(30, 1, 10)),
c9=floor(runif(30, 1, 10))
)
d
# A tibble: 30 × 9
c1 c2 c3 c4 c5 c6 c7 c8 c9
<dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl>
1 3 9 6 2 6 8 8 5 5
2 8 9 1 6 3 5 3 3 6
3 4 7 4 4 3 4 7 2 2
4 8 8 3 6 2 3 3 7 6
5 9 1 8 3 4 1 6 1 3
6 1 5 5 2 9 4 5 7 7
7 5 7 8 8 2 6 3 4 4
8 9 2 8 1 1 2 6 4 9
9 5 3 8 5 2 5 9 8 9
10 5 3 4 5 7 2 9 9 7
# … with 20 more rows
Now solve the problem
d %>%
pivot_longer(
everything(),
names_to="Column",
values_to="Value"
) %>%
group_by(Column, Value) %>%
summarise(N=n(), .groups="drop") %>%
pivot_wider(
names_from=Column,
values_from=N,
id_cols=Value,
values_fill=0
)
# A tibble: 9 × 10
Value c1 c2 c3 c4 c5 c6 c7 c8 c9
<dbl> <int> <int> <int> <int> <int> <int> <int> <int> <int>
1 1 3 2 3 2 3 1 0 3 1
2 2 1 7 3 4 4 4 3 3 3
3 3 4 4 2 3 6 2 7 3 3
4 4 1 5 6 3 3 7 3 5 4
5 5 5 2 2 5 1 5 3 3 5
6 6 4 1 3 5 3 5 6 2 4
7 7 3 3 3 1 4 3 1 5 3
8 8 2 3 6 1 3 3 2 3 3
9 9 7 3 2 6 3 0 5 3 4
Related
Hello I have a data frame of 245 columns but to add some sets and generate new columns try to do it recursively as follows
cl1<-sample(1:4,10,replace=TRUE)
cl2<-sample(1:4,10,replace=TRUE)
cl3<-sample(1:4,10,replace=TRUE)
cl4<-sample(1:4,10,replace=TRUE)
cl5<-sample(1:4,10,replace=TRUE)
cl6<-sample(1:4,10,replace=TRUE)
dat<-data.frame(cl1,cl2,cl3,cl4,cl5,cl6)
my intention is to add column 1 with column 3 and 5, likewise column 2 with 4 and 6 and in the end obtain a dataframe with two columns
and you should pay me something like that
I have programmed the following code
revisar<- function(a){
todos = list()
i=1
j=3
l=5
k=1
while(i<=2 ){
cl<-a[,i]
cl2<-a[,j]
cl3<-a[,l]
cl[is.na(cl)] <- 0
cl2[is.na(cl2)] <- 0
cl3[is.na(cl3)] <- 0
colu<-cl+cl2+cl3
col<-cbind(colu,colu)
i<-i+1
j<-j+1
l<-l+1
k<-k+1
}
return(col)
}
it turns out that it only returns column 2 repeated twice and I must replicate the same thing to join those 245 columns.7
I would like to know what is failing the example
base R
Literal programming:
with(dat, data.frame(s1 = cl1+cl3+cl5, s2 = cl2+cl4+cl6))
# s1 s2
# 1 7 11
# 2 7 7
# 3 4 11
# 4 4 10
# 5 9 8
# 6 12 5
# 7 7 6
# 8 7 10
# 9 4 9
# 10 6 5
Programmatically,
L <- list(s1 = c(1,3,5), s2 = c(2,4,6))
out <- data.frame(lapply(L, function(z) do.call(rowSums, list(as.matrix(dat[,z])))))
out
# s1 s2
# 1 7 11
# 2 7 7
# 3 4 11
# 4 4 10
# 5 9 8
# 6 12 5
# 7 7 6
# 8 7 10
# 9 4 9
# 10 6 5
dplyr
library(dplyr)
dat %>%
transmute(
s1 = rowSums(cbind(cl1, cl3, cl5)),
s2 = rowSums(cbind(cl2, cl4, cl6))
)
or programmatically using purrr:
purrr::map_dfc(L, ~ rowSums(dat[, .]))
Data
set.seed(42)
# your `dat` above
Here is an alternative general approach:
Here we sum all uneven columns -> s1 and
all even columns -> s2:
library(dplyr)
dat %>%
rowwise() %>%
mutate(s1 = sum(c_across(seq(1,ncol(dat),2)), na.rm = TRUE),
s2 = sum(c_across(seq(2,ncol(dat),2)), na.rm = TRUE))
cl1 cl2 cl3 cl4 cl5 cl6 s1 s2
<int> <int> <int> <int> <int> <int> <int> <int>
1 1 1 3 2 3 2 7 5
2 2 4 1 4 2 3 5 11
3 2 2 2 2 1 3 5 7
4 2 4 4 3 1 4 7 11
5 2 4 4 3 2 2 8 9
6 3 3 3 2 2 2 8 7
7 2 1 1 2 1 4 4 7
8 2 4 1 3 2 3 5 10
9 3 1 1 2 3 4 7 7
10 2 4 1 3 4 4 7 11
i need some help:
i got this df:
df <- data.frame(month = c(1,1,1,1,1,2,2,2,2,2),
day = c(1,2,3,4,5,1,2,3,4,5),
flow = c(2,5,7,8,5,4,6,7,9,2))
month day flow
1 1 1 2
2 1 2 5
3 1 3 7
4 1 4 8
5 1 5 5
6 2 1 4
7 2 2 6
8 2 3 7
9 2 4 9
10 2 5 2
but i want to know the day of min per month:
month day flow dayminflowofthemonth
1 1 1 2 1
2 1 2 5 1
3 1 3 7 1
4 1 4 8 1
5 1 5 5 1
6 2 1 4 5
7 2 2 6 5
8 2 3 7 5
9 2 4 9 5
10 2 5 2 5
this repetition is not a problem, i will use pivot fuction
tks people!
We can use which.min to return the index of 'min'imum 'flow' per group and use that to get the corresponding 'day' to create the column with mutate
library(dplyr)
df <- df %>%
group_by(month) %>%
mutate(dayminflowofthemonth = day[which.min(flow)]) %>%
ungroup
-output
df
# A tibble: 10 x 4
# month day flow dayminflowofthemonth
# <dbl> <dbl> <dbl> <dbl>
# 1 1 1 2 1
# 2 1 2 5 1
# 3 1 3 7 1
# 4 1 4 8 1
# 5 1 5 5 1
# 6 2 1 4 5
# 7 2 2 6 5
# 8 2 3 7 5
# 9 2 4 9 5
#10 2 5 2 5
Another option using indexing inside dplyr pipeline:
library(dplyr)
#Code
newdf <- df %>% group_by(month) %>% mutate(Val=day[flow==min(flow)][1])
Output:
# A tibble: 10 x 4
# Groups: month [2]
month day flow Val
<dbl> <dbl> <dbl> <dbl>
1 1 1 2 1
2 1 2 5 1
3 1 3 7 1
4 1 4 8 1
5 1 5 5 1
6 2 1 4 5
7 2 2 6 5
8 2 3 7 5
9 2 4 9 5
10 2 5 2 5
Here is a base R option using ave
transform(
df,
dayminflowofthemonth = ave(day*(ave(flow,month,FUN = min)==flow),month,FUN = max)
)
which gives
month day flow dayminflowofthemonth
1 1 1 2 1
2 1 2 5 1
3 1 3 7 1
4 1 4 8 1
5 1 5 5 1
6 2 1 4 5
7 2 2 6 5
8 2 3 7 5
9 2 4 9 5
10 2 5 2 5
One more base R approach:
df$dayminflowofthemonth <- by(
df,
df$month,
function(x) x$day[which.min(x$flow)]
)[df$month]
i've got some data in two columns:
# A tibble: 16 x 2
code niveau
<chr> <dbl>
1 A 1
2 1 2
3 2 2
4 3 2
5 4 2
6 5 2
7 B 1
8 6 2
9 7 2
My desired output is:
A tibble: 16 x 3
code niveau cat
<chr> <dbl> <chr>
1 A 1 A
2 1 2 A
3 2 2 A
4 3 2 A
5 4 2 A
6 5 2 A
7 B 1 B
8 6 2 B
I there a tidy way to convert these data without looping through it?
Here some dummy data:
data<-tibble(code=c('A', 1,2,3,4,5,'B', 6,7,8,9,'C',10,11,12,13), niveau=c(1, 2,2,2,2,2,1,2,2,2,2,1,2,2,2,2))
desired_output<-tibble(code=c('A', 1,2,3,4,5,'B', 6,7,8,9,'C',10,11,12,13), niveau=c(1, 2,2,2,2,2,1,2,2,2,2,1,2,2,2,2),
cat=c(rep('A', 6),rep('B', 5), rep('C', 5)))
Nicolas
Probably, you can create a new column cat and replace code values with NA where there is a number. We can then use fill to replace missing values with previous non-NA value.
library(dplyr)
data %>% mutate(cat = replace(code, grepl('\\d', code), NA)) %>% tidyr::fill(cat)
# A tibble: 16 x 3
# code niveau cat
# <chr> <dbl> <chr>
# 1 A 1 A
# 2 1 2 A
# 3 2 2 A
# 4 3 2 A
# 5 4 2 A
# 6 5 2 A
# 7 B 1 B
# 8 6 2 B
# 9 7 2 B
#10 8 2 B
#11 9 2 B
#12 C 1 C
#13 10 2 C
#14 11 2 C
#15 12 2 C
#16 13 2 C
We can use str_detect from stringr
library(dplyr)
library(stringr)
library(tidyr)
data %>%
mutate(cat = replace(code, str_detect(code, '\\d'), NA)) %>%
fill(cat)
I have the following data frame
Library(dplyr)
ID <- c(1,1,1,2,2,2,2,3,3)
Tag <- c(1,2,6,1,3,4,6,4,3)
Value <- c(5,9,3,3,5,6,4,8,9)
DF <- data.frame(ID,Tag,Value)
ID Tag Value
1 1 1 5
2 1 2 9
3 1 6 3
4 2 1 3
5 2 3 5
6 2 4 6
7 2 6 4
8 3 4 8
9 3 3 9
I would like to perform the following 1) group by rows ID 2) assign the Value corresponding to a specific Tag a new column. In the following example, I am assigning the Value of Tag 6 to a new column by ID
ID Tag Value New_Value
1 1 1 5 3
2 1 2 9 3
3 1 6 3 3
4 2 1 3 4
5 2 3 5 4
6 2 4 6 4
7 2 6 4 4
8 3 4 8 NA
9 3 3 9 NA
To the best of my knowledge, I need to subset the data in each group to get the Value for Tag 6. Here is my code and the error msg
DF %>% group_by(ID) %>% mutate(New_Value = select(filter(.,Tag==6),Value))
Adding missing grouping variables: `ID`
Error: Column `New_Value` is of unsupported class data.frame
Another possible solution is to create a new dataframe with IDs and Values for Tag 6 and join it with DF. However, I believe there is a better generic solution by only using dplyr.
I would appreciate it if you can help me understand how to perform a nested subset in this situation
Thank you
On the assumption that Tag is unique within groups, you could do:
library(dplyr)
DF %>%
group_by(ID) %>%
mutate(New_Value = ifelse(any(Tag == 6), Value[Tag == 6], NA))
# A tibble: 9 x 4
# Groups: ID [3]
ID Tag Value New_Value
<dbl> <dbl> <dbl> <dbl>
1 1 1 5 3
2 1 2 9 3
3 1 6 3 3
4 2 1 3 4
5 2 3 5 4
6 2 4 6 4
7 2 6 4 4
8 3 4 8 NA
9 3 3 9 NA
I'm looking for a dplyr or tidyr solution to split a dataset into n chunks. However, I do not want to have any single ID go into multiple chunks. That is, each ID should appear in only one chunk.
For example, imagine "test" below is an ID variable, and the dataset has many other columns.
test<-data.frame(id= c(1,2,3,4,4,4,4,4,6,7,8,9,9,9,9,10),
val = 1:16)
out <- test %>% select(id) %>% ntile(n = 3)
out
[1] 1 1 1 1 1 1 2 2 2 2 2 3 3 3 3 3
The ID=4 would end up in chunks 1 and 2. I am wondering how to code this so that all ID=4 end up in the same chunk (doesn't matter which one). I looked at the split function but could not find a way to do this.
The desired output would be something like
test[which(out==1),]
returning
id val
1 1 1
2 2 2
3 3 3
4 4 4
5 4 5
6 4 6
7 4 7
8 4 8
Then if I wanted to look at the second chunk, I would call something like test[which(out==2),], and so on up to out==n. I only want to deal with one chunk at a time. I don't need to create all n chunks simultaneously.
You need to create a data frame, then use group_by and mutate to add columns:
test<-data_frame(id = c(1,2,3,4,4,4,4,4,6,7,8,9,9,9,9,10),
value = 1:16)
out <- test %>%
mutate(new_column = ntile(id,3))
out
# A tibble: 16 x 3
id value new_column
<dbl> <int> <int>
1 1 1 1
2 2 2 1
3 3 3 1
4 4 4 1
5 4 5 1
6 4 6 1
7 4 7 2
8 4 8 2
9 6 9 2
10 7 10 2
11 8 11 2
12 9 12 3
13 9 13 3
14 9 14 3
15 9 15 3
16 10 16 3
Or given Frank's comment you could run the ntile function on distinct/unique values of the id - then join the original table back on id:
test<-data_frame(id = c(1,2,3,4,4,4,4,4,6,7,8,9,9,9,9,10),
value = 1:16)
out <- test %>%
distinct(id) %>%
mutate(new_column = ntile(id,3)) %>%
right_join(test, by = "id")
out
# A tibble: 16 x 3
id new_column value
<dbl> <int> <int>
1 1 1 1
2 2 1 2
3 3 1 3
4 4 2 4
5 4 2 5
6 4 2 6
7 4 2 7
8 4 2 8
9 6 2 9
10 7 2 10
11 8 3 11
12 9 3 12
13 9 3 13
14 9 3 14
15 9 3 15
16 10 3 16