Q:

Count number of previous observations which match on 10 variable values with at most 3 exceptions

I have a dataset of about 10,000 observations structured as follows:
    df<-data.frame(Company=c("Corporation 1","Corporation 2","Corporation 3","Corporation 4","Corporation 5"),
           Year=c(2000,2001,2001,2002,2003),
           category1=c(1,1,0,0,1),
           category2=c(0,1,0,0,0),
           category3=c(1,0,1,0,0),
           category4=c(1,1,0,1,1),
           category5=c(1,1,0,1,1),
           category6=c(1,1,0,1,1),
           category7=c(0,0,1,0,0),
           category8=c(1,1,1,0,1),
           category9=c(1,1,1,0,1),
           category10=c(1,1,0,1,1))

I would like a variable that counts the number of other observations who match the values in categories 1-10 with at most 3 exceptions, and only for corporations that existed in prior years. The result would be the variable named "count" in the following dataframe:
df<-data.frame(Company=c("Corporation 1","Corporation 2","Corporation 3","Corporation 4","Corporation 5"),
           Year=c(2000,2001,2001,2002,2003),
           category1=c(1,1,0,0,1),
           category2=c(0,1,0,0,0),
           category3=c(1,0,1,0,0),
           category4=c(1,1,0,1,1),
           category5=c(1,1,0,1,1),
           category6=c(1,1,0,1,1),
           category7=c(0,0,1,0,0),
           category8=c(1,1,1,0,1),
           category9=c(1,1,1,0,1),
           category10=c(1,1,0,1,1),
           count=c(0,1,0,0,3))

Corporation 5 matches 3 of the previous corporations' category values (with at most 3 exceptions), Corporation 4 matches none, Corporation 3 matches none, Corporation 2 matches one, and of course Corporation 1 doesn't match any because there are no prior years.
+1 for an answer using dplyr.
Thanks in advance.

A:

df<-data.frame(Company=c("Corporation 1","Corporation 2","Corporation 3","Corporation 4","Corporation 5"),
               Year=c(2000,2001,2001,2002,2003),
               category1=c(1,1,0,0,1),
               category2=c(0,1,0,0,0),
               category3=c(1,0,1,0,0),
               category4=c(1,1,0,1,1),
               category5=c(1,1,0,1,1),
               category6=c(1,1,0,1,1),
               category7=c(0,0,1,0,0),
               category8=c(1,1,1,0,1),
               category9=c(1,1,1,0,1),
               category10=c(1,1,0,1,1))

library(dplyr)
library(tidyr)

# function that gets 2 company names and counts number of matching elements 
f = function(x,y) {

v1 = df %>% 
  filter(Company == x) %>%
  select(category1:category10) %>%
  unlist() 

v2 = df %>% 
  filter(Company == y) %>%
  select(category1:category10) %>%
  unlist()

sum(v1 == v2)
}

f = Vectorize(f)

expand.grid(c1=df$Company, c2=df$Company) %>% # create all pairwise combinations
  filter(as.numeric(c1) > as.numeric(c2)) %>% # keep unique pairs (categories have to be factor variables)
  mutate(score = f(c1,c2)) %>%                # apply function and get scores
  arrange(c1) %>%                             # order by first category (only to visualise)
  mutate(IsMatching = score >= 7)             # flag if there is a match

#               c1            c2 score IsMatching
# 1  Corporation 2 Corporation 1     8       TRUE
# 2  Corporation 3 Corporation 1     4      FALSE
# 3  Corporation 3 Corporation 2     2      FALSE
# 4  Corporation 4 Corporation 1     6      FALSE
# 5  Corporation 4 Corporation 2     6      FALSE
# 6  Corporation 4 Corporation 3     2      FALSE
# 7  Corporation 5 Corporation 1     9       TRUE
# 8  Corporation 5 Corporation 2     9       TRUE
# 9  Corporation 5 Corporation 3     3      FALSE
# 10 Corporation 5 Corporation 4     7       TRUE

You can use the above table to see best/worst matches and generally any pairwise match info.
For your ideal output you just need to add some more commands in the end to count the number of matches per company:
expand.grid(c1=df$Company, c2=df$Company) %>% 
  filter(as.numeric(c1) > as.numeric(c2)) %>% 
  mutate(score = f(c1,c2)) %>%              
  arrange(c1) %>%                             
  mutate(IsMatching = score >= 7) %>%
  group_by(c1) %>%
  summarise(NumMatches = sum(IsMatching))

# # A tibble: 4 x 2
#              c1 NumMatches
#          <fctr>      <int>
# 1 Corporation 2          1
# 2 Corporation 3          0
# 3 Corporation 4          0
# 4 Corporation 5          3

