I have a table with two identifier columns w and x. The columns y brings order to the column z, which is the destination variable. Every sequence identified by w and x has at least two rows and all distinct values in z. I want to count for each of the zs how often they occur in the sequences in which position. Position-wise I'm interested in whether it's the first, last or any other.
My approach in R's dplyris the following (for those unfamiliar with %>%, it's the pipe operator that takes the left hand output and puts it as first argument into the right hand function, you can read it as "and then"):
library(tidyverse)
library(reticulate)
data <- tribble(
~w, ~x, ~y, ~z,
1, 1, 1, "a",
1, 1, 2, "b",
1, 2, 1, "b",
1, 2, 2, "a",
1, 2, 3, "c",
1, 2, 4, "d",
2, 1, 1, "b",
2, 1, 2, "a",
2, 1, 3, "c",
2, 1, 4, "d"
)
First I add a sequence index to each ordered w and x groups and a marker that determine the categorical position of z in the sequence from it (the position_in_sequence).
data_with_markers <-
data %>%
group_by(w, x) %>%
arrange(y, .by_group = TRUE) %>%
mutate(
sequence_id = row_number(),
position_in_sequence = case_when(
sequence_id == first(sequence_id) ~ "first",
sequence_id == last(sequence_id) ~ "last",
TRUE ~ "other" # this is the "else"
)
) %>%
ungroup()
>data_with_markers
# A tibble: 10 x 6
w x y z sequence_id position_in_sequence
<dbl> <dbl> <dbl> <chr> <int> <chr>
1 1 1 1 a 1 first
2 1 1 2 b 2 last
3 1 2 1 b 1 first
4 1 2 2 a 2 other
5 1 2 3 c 3 other
6 1 2 4 d 4 last
7 2 1 1 b 1 first
8 2 1 2 a 2 other
9 2 1 3 c 3 other
10 2 1 4 d 4 last
Then I do a simple count by the position_in_sequence and z.
data_summary <- data_with_markers %>%
group_by(position_in_sequence, z) %>%
count() %>%
ungroup()
> data_summary
# A tibble: 6 x 3
position_in_sequence z n
<chr> <chr> <int>
1 first a 1
2 first b 2
3 last b 1
4 last d 2
5 other a 2
6 other c 2
For pandas, I'm stuck with getting the position_in_sequence variable (I'm using the reticulate package here):
import pandas as pd
data = r.data
data
w x y z
0 1.0 1.0 1.0 a
1 1.0 1.0 2.0 b
2 1.0 2.0 1.0 b
3 1.0 2.0 2.0 a
4 1.0 2.0 3.0 c
5 1.0 2.0 4.0 d
6 2.0 1.0 1.0 b
7 2.0 1.0 2.0 a
8 2.0 1.0 3.0 c
9 2.0 1.0 4.0 d
data_sorted = data.sort_values(['w', 'x', 'y'])
data_sorted['sequence_id'] = data_sorted.groupby(['w', 'x']).cumcount() + 1
data_sorted
w x y z sequence_id
0 1.0 1.0 1.0 a 1
1 1.0 1.0 2.0 b 2
2 1.0 2.0 1.0 b 1
3 1.0 2.0 2.0 a 2
4 1.0 2.0 3.0 c 3
5 1.0 2.0 4.0 d 4
6 2.0 1.0 1.0 b 1
7 2.0 1.0 2.0 a 2
8 2.0 1.0 3.0 c 3
9 2.0 1.0 4.0 d 4
I fiddled around with .apply but I would need to access the column sequence_id at a certain row and the column's min and max at the same time to compare, but I didn't get it to work.