# loading all necessary libraries

library(arules)
library(arulesViz)
library(arulesCBA)
library(kableExtra)
library(ggplot2)

Introduction

The aim of the study is to find and analyze association rules for a set of diseases and their symptoms. Such an analysis should make it possible to study which disease symptoms occur in combination with other symptoms, so that it will be simpler to predict what may be ailing a particular patient and what other symptoms can be expected for that patient.

Review of the data

The study used data on diseases and their symptoms. The dataset includes 41 different diseases, each disease has been assigned from 3 to 17 different symptoms (131 symptoms were listed), so there are 172 different objects in the dataset. The dataset contains 4920 observations of various repeated cases of diseases and their various symptoms. The dataset comes from the kaggle website and goes under the name ‘Disease and Symptoms dataset’ .

data <- read.csv("DiseaseAndSymptoms.csv", sep=",")
data <- data[sample(nrow(data)), ]
write.csv(data, file = "DiseaseAndSymptoms_trans.csv", row.names = FALSE)

#Loading data as a transaction
trans <- read.transactions("DiseaseAndSymptoms_trans.csv", format="basket", sep=",", header = TRUE, skip=0)
trans
## transactions in sparse format with
##  4920 transactions (rows) and
##  172 items (columns)

The following shows data on diseases and their symptoms in the form of transactional data and the size of each “basket.” From the data below, we can read that different disease observations were assigned different amounts of symptoms. (The names of diseases are written with a capital letter, and symptoms with a lowercase letter).

inspect(trans[0:5])
##     items                          
## [1] {breathlessness,               
##      chest_pain,                   
##      Heart attack,                 
##      sweating,                     
##      vomiting}                     
## [2] {Arthritis,                    
##      movement_stiffness,           
##      muscle_weakness,              
##      painful_walking,              
##      stiff_neck,                   
##      swelling_joints}              
## [3] {abdominal_pain,               
##      dark_urine,                   
##      fatigue,                      
##      high_fever,                   
##      itching,                      
##      Jaundice,                     
##      vomiting,                     
##      weight_loss,                  
##      yellowish_skin}               
## [4] {anxiety,                      
##      blurred_and_distorted_vision, 
##      drying_and_tingling_lips,     
##      excessive_hunger,             
##      fatigue,                      
##      headache,                     
##      Hypoglycemia,                 
##      irritability,                 
##      nausea,                       
##      palpitations,                 
##      slurred_speech,               
##      sweating,                     
##      vomiting}                     
## [5] {abnormal_menstruation,        
##      brittle_nails,                
##      cold_hands_and_feets,         
##      depression,                   
##      dizziness,                    
##      enlarged_thyroid,             
##      fatigue,                      
##      Hypothyroidism,               
##      irritability,                 
##      lethargy,                     
##      mood_swings,                  
##      puffy_face_and_eyes,          
##      swollen_extremeties,          
##      weight_gain}
size(trans[1:100])
##   [1]  5  6  9 13 14 11 12  7 13 12 10  7 13  6 12  7 18  9  6  7  7  5  5  5  6
##  [26]  6  7  4  6 12  9  6  6  9  6 13 11 17  5  6  4  5  5 10 15  7 14 13  7  7
##  [51] 12  5  7  7  8  8 10  7  8  5  4  8  6 13 12  6  6  7 12  6 14  9  6  5 18
##  [76]  6  6 14 12 17  7  6  5 17  9 11 12 15 11  5  7  8  6 12 17  5  6  5  9  6

The basic statistics of the dataset are presented below. We can read that it consists of 4920 observations and 172 elements (symptoms), the number of elements per observation is also presented. The largest number of observations consists of 5 elements (disease + 4 symptoms), with most diseases having between 4 and 6 symptoms assigned.

summary(trans)
## transactions as itemMatrix in sparse format with
##  4920 rows (elements/itemsets/transactions) and
##  172 columns (items) and a density of 0.04912082 
## 
## most frequent items:
##          fatigue         vomiting       high_fever loss_of_appetite 
##             1932             1914             1362             1152 
##           nausea          (Other) 
##             1146            34062 
## 
## element (itemset/transaction) length distribution:
## sizes
##   4   5   6   7   8   9  10  11  12  13  14  15  16  17  18 
## 348 858 780 666 324 252 180 318 450 240 198  66  48 120  72 
## 
##    Min. 1st Qu.  Median    Mean 3rd Qu.    Max. 
##   4.000   6.000   7.000   8.449  11.000  18.000 
## 
## includes extended item information - examples:
##                                    labels
## 1 (vertigo) Paroymsal  Positional Vertigo
## 2                          abdominal_pain
## 3                   abnormal_menstruation

The chart below shows the most common elements (symptoms) in the collection sorted by their frequency. As can be seen, each of the 15 most common elements in the set has a frequency of more than 10%, with the most common symptoms, fatigue and vomiting, appearing in almost 40% of observations each.

itemFrequencyPlot(trans, topN = 15)

The graph below shows the symptoms (horizontal axis) present in each observation (vertical axis), with the number of observations narrowed to 100 to increase the readability of the graph. The graph does not show any patterns, but we can see on the graph that some symptoms occur in a large number of observations ( vertical lines from black cells on the graph)

image(sample(trans, 100))

The Apriori algorithm

After analyzing the data in the previous chapter, an attempt was made to find association rules in the dataset. For this purpose, the Apriori algorithm was used. The algorithm was used with the following parameter values: minimum support 0.1, minimum confidence 0.75. Using this algorithm, 52 association rules were obtained.

In a table below we can see some of statistics of created rules. We can see that most of the created rules consist of 3 objects, others consist of 2 or 4. In the table you can also see information about the values of the basic statistics for these rules - support, confidence and lift

associationRules <- associationRules <- apriori(trans, parameter = list(supp = 0.1, conf = 0.75, minlen = 2))
## Apriori
## 
## Parameter specification:
##  confidence minval smax arem  aval originalSupport maxtime support minlen
##        0.75    0.1    1 none FALSE            TRUE       5     0.1      2
##  maxlen target  ext
##      10  rules TRUE
## 
## Algorithmic control:
##  filter tree heap memopt load sort verbose
##     0.1 TRUE TRUE  FALSE TRUE    2    TRUE
## 
## Absolute minimum support count: 492 
## 
## set item appearances ...[0 item(s)] done [0.00s].
## set transactions ...[172 item(s), 4920 transaction(s)] done [0.00s].
## sorting and recoding items ... [19 item(s)] done [0.00s].
## creating transaction tree ... done [0.00s].
## checking subsets of size 1 2 3 4 done [0.00s].
## writing ... [52 rule(s)] done [0.00s].
## creating S4 object  ... done [0.00s].
summary(associationRules)
## set of 52 rules
## 
## rule length distribution (lhs + rhs):sizes
##  2  3  4 
## 10 34  8 
## 
##    Min. 1st Qu.  Median    Mean 3rd Qu.    Max. 
##   2.000   3.000   3.000   2.962   3.000   4.000 
## 
## summary of quality measures:
##     support         confidence        coverage           lift      
##  Min.   :0.1000   Min.   :0.7748   Min.   :0.1049   Min.   :2.028  
##  1st Qu.:0.1049   1st Qu.:0.7995   1st Qu.:0.1119   1st Qu.:2.838  
##  Median :0.1061   Median :0.9435   Median :0.1323   Median :4.078  
##  Mean   :0.1147   Mean   :0.8816   Mean   :0.1311   Mean   :3.821  
##  3rd Qu.:0.1113   3rd Qu.:0.9547   3rd Qu.:0.1354   3rd Qu.:4.558  
##  Max.   :0.1988   Max.   :0.9770   Max.   :0.2329   Max.   :5.891  
##      count      
##  Min.   :492.0  
##  1st Qu.:516.0  
##  Median :522.0  
##  Mean   :564.2  
##  3rd Qu.:547.5  
##  Max.   :978.0  
## 
## mining info:
##   data ntransactions support confidence
##  trans          4920     0.1       0.75
##                                                                          call
##  apriori(data = trans, parameter = list(supp = 0.1, conf = 0.75, minlen = 2))

The sets of 5 rules with the highest values of the statistics support, confidence and lift, respectively, are shown below.

We can observe that the symptom pairs with the highest support value are nausea and vomiting (support = 0.2) and abdominal_pain and vomiting (support = 0.18). This means that vomiting combined with nausea appear together in 20% of all disease observations in the collection.

We can also observe that the created rules have a very high confidence level (the rules with the highest confidence coefficient reach a value above 0.95). These are mainly rules containing the symptom “yellowing_of_eyes” in combination with other symptoms such as loss of appetite or vomiting. For example, this means that 97% of patients with symptoms of vomiting and yellowing of eyes also have loss of appetite.

Rules with the highest lift value also consist of similar symptoms to those with a high confidence value. The lift value can be interpreted as follows: for the second rule shown in the table, lift = 5.8. This means that people with symptoms of loss_of_appetite and yellowish_skin are 5.8 times more likely to have yellowing_of_eyes compared to other observed individuals.

top_rules_support <- inspect(sort(associationRules, by = "support")[1:5])
kable(top_rules_support)
lhs rhs support confidence coverage lift count
[1] {nausea} => {vomiting} 0.1987805 0.8534031 0.2329268 2.193701 978
[2] {abdominal_pain} => {vomiting} 0.1768293 0.8430233 0.2097561 2.167019 870
[3] {yellowing_of_eyes} => {loss_of_appetite} 0.1597561 0.9632353 0.1658537 4.113817 786
[4] {yellowish_skin} => {abdominal_pain} 0.1548780 0.8355263 0.1853659 3.983323 762
[5] {malaise} => {fatigue} 0.1353659 0.9487179 0.1426829 2.415990 666
inspect(sort(associationRules, by = "confidence")[1:5])
##     lhs                    rhs                   support confidence  coverage     lift count
## [1] {abdominal_pain,                                                                        
##      loss_of_appetite,                                                                      
##      yellowish_skin}    => {yellowing_of_eyes} 0.1036585  0.9770115 0.1060976 5.890805   510
## [2] {vomiting,                                                                              
##      yellowing_of_eyes} => {loss_of_appetite}  0.1097561  0.9677419 0.1134146 4.133065   540
## [3] {fatigue,                                                                               
##      yellowing_of_eyes} => {loss_of_appetite}  0.1085366  0.9673913 0.1121951 4.131567   534
## [4] {loss_of_appetite,                                                                      
##      yellowish_skin}    => {yellowing_of_eyes} 0.1280488  0.9633028 0.1329268 5.808149   630
## [5] {yellowing_of_eyes} => {loss_of_appetite}  0.1597561  0.9632353 0.1658537 4.113817   786
top_rules_lift <- inspect(sort(associationRules, by = "lift")[1:5])
##     lhs                    rhs                   support confidence  coverage     lift count
## [1] {abdominal_pain,                                                                        
##      loss_of_appetite,                                                                      
##      yellowish_skin}    => {yellowing_of_eyes} 0.1036585  0.9770115 0.1060976 5.890805   510
## [2] {loss_of_appetite,                                                                      
##      yellowish_skin}    => {yellowing_of_eyes} 0.1280488  0.9633028 0.1329268 5.808149   630
## [3] {nausea,                                                                                
##      yellowish_skin}    => {yellowing_of_eyes} 0.1048780  0.9555556 0.1097561 5.761438   516
## [4] {loss_of_appetite,                                                                      
##      nausea,                                                                                
##      yellowish_skin}    => {yellowing_of_eyes} 0.1000000  0.9534884 0.1048780 5.748974   492
## [5] {dark_urine}        => {yellowish_skin}    0.1097561  0.9473684 0.1158537 5.110803   540

Below are two charts showing the most important features of all 52 rules and the relationships between them. The first one (scatter plot) shows the relationship between the level of support (horizontal axis), confidence (vertical axis) and lift (color of points) for the created rules. We can observe on it that most of the rules can be divided into two groups - those with a high level of confidence (around 0.95) and lift, and those with a lower level of confidence (0.8) and lift. Most rules, on the other hand, have a similar level of support (around 0.1), with a few exceptions with much higher support.

The second chart shows the relationships between objects (symptoms) included in the rules. It allows us to observe the relationships between objects and understand how rules are formed. In this chart, we can observe that the most relationships on the graph are between the following symptoms: loss of appetite, yellowish skin, nausea, vomiting or abdominal pain. These rules are also characterized by a high level of support and lift. The second group of rule-forming objects are symptoms common to most diseases, such as high fever or fatigue. However, they form “weaker” rules than those in the previous group (lower lift and support)

plot(associationRules)

plot(associationRules, method="graph", control=list(
  edges = ggraph::geom_edge_link(
    color = "black",
    arrow = arrow(length = unit(2, "mm"), angle = 20, type = "closed"),
    alpha = .2))) 

The above analysis allowed us to find association rules for symptoms occurring in the individuals in question together. Based on it, we can determine the groups of symptoms that are most likely to occur together. One such group that we were able to distinguish is symptoms related to jaundice and abdominal diseases.

We can supplement this analysis by examining how the selected single symptom implies the occurrence of other symptoms in the patient or, conversely, what symptoms must be present in the patient for the selected symptom to occur in the patient.

Analysis of fever as a consequent

The analysis for the symptom fever as consequent is shown below. It is designed to examine what symptoms must be present in a patient for a high fever to occur. To do this, an apriori algorithm was used with minimum parameter values: support = 0.075, confidence = 0.5. 14 rules were successfully obtained, the following presented 6 rules with the highest confidence value.

rules.fever<-apriori(data=trans, parameter = list(supp = 0.075, conf = 0.5, minlen =2),
                       appearance=list(default="lhs", rhs="high_fever"), control=list(verbose=F)) 
rules.fever.byconf<-sort(rules.fever, by="confidence", decreasing=TRUE)
rules.fever.byconf_head <- inspect(head(rules.fever.byconf))
rules.fever
## set of 14 rules
kable(rules.fever.byconf_head)
lhs rhs support confidence coverage lift count
[1] {chills, headache} => {high_fever} 0.0865854 0.9594595 0.0902439 3.465889 426
[2] {chills, vomiting} => {high_fever} 0.0853659 0.9589041 0.0890244 3.463883 420
[3] {chills, fatigue} => {high_fever} 0.1073171 0.9565217 0.1121951 3.455277 528
[4] {chills, malaise} => {high_fever} 0.0841463 0.9452055 0.0890244 3.414399 414
[5] {cough, fatigue} => {high_fever} 0.0804878 0.9428571 0.0853659 3.405916 396
[6] {chills, fatigue, malaise} => {high_fever} 0.0792683 0.9420290 0.0841463 3.402924 390

The following charts show the relationships between the symptoms leading to the fever symptom. From the charts, we can determine that if a patient has chills, malaise or fatigue (or a pair of these symptoms at once) then there is a good chance that he or she also has a fever. This is also confirmed by the high lift value for rules containing symptom pairs {chills, headache}, {chills, vomiting} and {chills, fatigue}.

plot(rules.fever, method="graph", control=list(
  edges = ggraph::geom_edge_link(
    color = "black",
    arrow = arrow(length = unit(2, "mm"), angle = 20, type = "closed"),
    alpha = .2))) 

plot(rules.fever, method="paracoord", control=list(reorder=TRUE))

Analysis of fever as a cause

The following analysis is based on finding the symptoms possessed by the patient if he is known to have a fever. This analysis will predict the symptoms that may appear in a patient who has a fever. To do this, an apriori algorithm was used with minimum parameter values: support = 0.05, confidence = 0.2. 16 rules were successfully obtained, the following presented 6 rules with the highest confidence value.

rules.fever2<-apriori(data=trans, parameter = list(supp = 0.05, conf = 0.2, minlen =2),
                     appearance=list(default="rhs", lhs="high_fever"), control=list(verbose=F)) 
rules.fever.byconf2<-sort(rules.fever2, by="confidence", decreasing=TRUE)
rules.fever.byconf2_head <- inspect(head(rules.fever.byconf2))
rules.fever2
## set of 16 rules
kable(rules.fever.byconf2_head)
lhs rhs support confidence coverage lift count
[1] {high_fever} => {fatigue} 0.1987805 0.7180617 0.2768293 1.828604 978
[2] {high_fever} => {chills} 0.1341463 0.4845815 0.2768293 2.987645 660
[3] {high_fever} => {vomiting} 0.1329268 0.4801762 0.2768293 1.234309 654
[4] {high_fever} => {malaise} 0.1121951 0.4052863 0.2768293 2.840468 552
[5] {high_fever} => {headache} 0.1121951 0.4052863 0.2768293 1.758385 552
[6] {high_fever} => {loss_of_appetite} 0.0914634 0.3303965 0.2768293 1.411068 450

The charts below show the formation of rules when one of the symptoms is known to be fever. As we can see, only rules consisting of two objects high fever + one selected symptom were formed. In the graph you can also observe selected statistics of the created rules. The highest lift values were achieved by rules containing phlegm and swelled lymph nodes (separately), while fatigue or chills were most often paired with fever (rules with the highest support value).

plot(rules.fever2, method="graph", control=list(
  edges = ggraph::geom_edge_link(
    color = "black",
    arrow = arrow(length = unit(2, "mm"), angle = 20, type = "closed"),
    alpha = .2))) 

plot(rules.fever2, method="paracoord", control=list(reorder=TRUE))

Conclusions

The study conducted an analysis involving the creation of association rules for a set of diseases and their symptoms. It was possible to create a set of such rules that allowed the study to examine which symptoms most often appear together with each other. An additional analysis was also conducted for the fever object to see which symptoms most often occur if a patient has a fever and vice versa, i.e. what symptoms must be present in a patient for a high fever to occur.