Pull out all transactions after a bad credit found

Viewed 61

Below is a small dataset of transaction records, with ID, DATE of the month, dummy variable of Bad_Credit or not. I would like to pull out all the transactions after a bad credit start. The OUTPUT column indicate the correct result, which is row 1,2,3,5,6,8,10.

This is just an example, there could be thousands of rows. SQL, R, SPSS will all work. Thank you.

DATE ID Bad_CREDIT OUTPUT
12 A 1 1
15 A 1 1
18 A 0 1
2 B 0 0
10 B 1 1
20 B 0 1
5 C 0 0
15 C 1 1
1 D 0 0
9 E 1 1
5 Answers

You can arrange the data by ID and DATE and for each ID assign 0 if the first value of Bad_CREDIT is 0.

library(dplyr)

df %>%
  arrange(ID, DATE) %>%
  group_by(ID) %>%
  mutate(OUTPUT = as.integer(!(first(Bad_CREDIT) == 0 & row_number() == 1)))

#    DATE ID    Bad_CREDIT OUTPUT
#   <int> <chr>      <int>  <int>
# 1    12 A              1      1
# 2    15 A              1      1
# 3    18 A              0      1
# 4     2 B              0      0
# 5    10 B              1      1
# 6    20 B              0      1
# 7     5 C              0      0
# 8    15 C              1      1
# 9     1 D              0      0
#10     9 E              1      1

data

df <- structure(list(DATE = c(12L, 15L, 18L, 2L, 10L, 20L, 5L, 15L, 
1L, 9L), ID = c("A", "A", "A", "B", "B", "B", "C", "C", "D", 
"E"), Bad_CREDIT = c(1L, 1L, 0L, 0L, 1L, 0L, 0L, 1L, 0L, 1L)), 
row.names = c(NA, -10L), class = "data.frame")

If I understand correctly, you can use window functions:

select t.*
from (select t.*,
             min(case when bad_credit = 1 then date end) over (partition by id) as min_bd_date
      from t
     ) t
where date >= min_bd_date;

You can also do this with a correlated subquery:

select t.*
from t
where t.date >= (select min(t2.date)
                 from t t2
                 where t2.id = t.id and
                       t2.bad_credit = 1
                );

If this is in a database, then I think SQL is likely the better place to address this. However, if you already have it in R, then ...

Here's an R method, using dplyr:

library(dplyr)
dat %>%
  group_by(ID) %>%
  mutate(OUTPUT2 = +cumany(Bad_CREDIT)) %>%
  ungroup()
# # A tibble: 10 x 5
#     DATE ID    Bad_CREDIT OUTPUT OUTPUT2
#    <int> <chr>      <int>  <int>   <int>
#  1    12 A              1      1       1
#  2    15 A              1      1       1
#  3    18 A              0      1       1
#  4     2 B              0      0       0
#  5    10 B              1      1       1
#  6    20 B              0      1       1
#  7     5 C              0      0       0
#  8    15 C              1      1       1
#  9     1 D              0      0       0
# 10     9 E              1      1       1

Because this is effectively a simple grouping operation, then base R and data.table solutions are as straight-forward.

+ave(dat$Bad_CREDIT, dat$ID, FUN=cumany)
#  [1] 1 1 1 0 1 1 0 1 0 1

library(data.table)
datDT <- as.data.table(dat)
datDT[, OUTPUT2 := +cumany(Bad_CREDIT), by = .(ID)]

You can use EXISTS as follows:

select t.* from your_table t
where exists
     (select 1
        from your_table tt
       where t.id = tt.id
         and t.date >= tt.date 
         and tt.bad_credit = 1);

This is for SPSS:

sort cases by ID date.
compute PullOut=Bad_CREDIT.
if $casenum>1 and ID=lag(ID) and lag(PullOut)=1 PullOut=1.
exe.
Related