Initializations

This cell loads the required packages needed below.

In [3]:
import pandas as pd
import numpy as np
import numpy.linalg as la
import scipy
import re
import random
from sklearn.preprocessing import normalize
import matplotlib.pyplot as plt
import matplotlib.colors as colors
import matplotlib.cm as cmx
import networkx as nx
import json
import os
import seaborn as sns
print np.version.version
print pd.version.version
from scipy.stats import kstest
from scipy.stats import ks_2samp
from scipy.stats import anderson_ksamp


%matplotlib inline

pd.set_option('display.max_columns', 300)
np.set_printoptions(linewidth=250)
np.set_printoptions(precision=3)

Import Data

Performs manual import and initial cleaning of data by converting all pain outcomes into a single 12-point scale and then selecting only those variables necessary.

In [8]:
#Manual import of data
d='insert file here'
dfprelim = pd.read_csv(d)
df = dfprelim
df = df[df.Pain_Scale != 'Off unit']
df = df.dropna(subset=['Pain_Scale'])
df['NRS'] = df['Pain_Scale']
df['NRS'].replace(['Asleep','asleep'],'11', inplace=True)
df[['id','NRS']]=df[['id','NRS']].astype(int)
dfcleanprelim=df
df=df[['id','NRS','Age_Group','Gender','CCSGroup_Label_CPT_1']]

Describe Dataset

This code aggregates the dataset by group, and then reports the characteristics of the dataset.

In [9]:
dfd = dfcleanprelim[['id','Age_Group','Age','Gender','CCSGroup_Label_CPT_1']].groupby('id').agg(lambda x:x.value_counts().index[0])
In [10]:
print 'Number of Pain Scores'; print;
print df.describe(); print;
print 'Age:' 
print dfd['Age'].describe(); print;
print 'Age Group:'
print dfd['Age_Group'].describe(); print;
print dfd['Age_Group'].value_counts(); print;
print 'Gender:'; print dfd['Gender'].describe(); print;
print dfd['Gender'].value_counts();print;
print 'NRS (all obs):'; print;

print 'CCSGroup_Label_CPT_1:'; print dfd['CCSGroup_Label_CPT_1'].describe(); print;
print dfd['CCSGroup_Label_CPT_1'].value_counts();print;

print df['NRS'].describe(); print;
print dfcleanprelim['Pain_Scale'].describe(); print; 
print dfcleanprelim['Pain_Scale'].value_counts()
print df['NRS'].value_counts()
Number of Pain Scores

                  id            NRS
count  476108.000000  476108.000000
mean     4180.465478       4.695340
std      2359.507333       4.006207
min         1.000000       0.000000
25%      2203.000000       0.000000
50%      4238.000000       5.000000
75%      6151.000000       8.000000
max      8346.000000      11.000000

Age:
count    8346.000000
mean       56.125689
std        16.258735
min        21.000000
25%        45.000000
50%        57.000000
75%        68.000000
max        97.000000
dtype: float64

Age Group:
count      8346
unique        4
top       40-64
freq       4029
dtype: object

40-64            4029
65-84            2656
21-39            1460
85 or Greater     201
dtype: int64

Gender:
count       8346
unique         2
top       FEMALE
freq        4193
dtype: object

FEMALE    4193
MALE      4153
dtype: int64

NRS (all obs):

CCSGroup_Label_CPT_1:
count                                   8346
unique                                   147
top       Hip replacement, total and partial
freq                                     299
dtype: object

Hip replacement, total and partial                  299
Incision and excision of CNS                        292
Arthroplasty knee                                   281
Other OR therapeutic nervous system procedures      274
No procedure                                        270
Laminectomy, excision intervertebral disc           261
Exploratory laparotomy                              242
Heart valve procedures                              227
Spinal fusion                                       226
Treatment, fracture or dislocation of hip and fe    221
Treatment, fracture or dislocation of lower extr    214
Skin graft                                          209
Hysterectomy, abdominal and vaginal                 200
Aortic resection, replacement or anastomosis        190
Debridement of wound, infection or burn             167
...
#Lower bounded procedures with singular counts manually removed from output. Please contact author for additional information.

Length: 147, dtype: int64

count    476108.000000
mean          4.695340
std           4.006207
min           0.000000
25%           0.000000
50%           5.000000
75%           8.000000
max          11.000000
dtype: float64

count     476108
unique        13
top            0
freq      142485
dtype: object

0         142485
Asleep     66440
5          40375
8          34016
7          33367
6          32036
4          30344
3          28250
2          24709
10         20981
9          14549
1           8555
asleep         1
dtype: int64
0     142485
11     66441
5      40375
8      34016
7      33367
6      32036
4      30344
3      28250
2      24709
10     20981
9      14549
1       8555
dtype: int64
In [12]:
x = df['NRS'].value_counts().sort_index()
print x
print x.plot(kind='bar')
0     142485
1       8555
2      24709
3      28250
4      30344
5      40375
6      32036
7      33367
8      34016
9      14549
10     20981
11     66441
dtype: int64
Axes(0.125,0.125;0.775x0.775)

Stratification of data by age and sex. We then list the most common types of surgery by the CCS group label, and pick five amongst the most common types.

In [15]:
df_male = df.ix[df.Gender=='MALE', ['id','NRS','Gender']]
df_female = df.ix[df.Gender=='FEMALE', ['id','NRS','Gender']]

df_21_39 = df.ix[df.Age_Group=='21-39', ['id','NRS','Age_Group']]
df_40_64 = df.ix[df.Age_Group=='40-64', ['id','NRS','Age_Group']]
df_65_84 = df.ix[df.Age_Group=='65-84', ['id','NRS','Age_Group']]
df_85_orGreater = df.ix[df.Age_Group=='85 or Greater', ['id','NRS','Age_Group']]

q= df[['id','CCSGroup_Label_CPT_1']].groupby('id').agg(lambda x:x.value_counts().index[0])
print q.groupby('CCSGroup_Label_CPT_1').size().order(ascending=False).head(20)

df_THA = df.ix[df.CCSGroup_Label_CPT_1=='Hip replacement, total and partial', ['id','NRS','CCSGroup_Label_CPT_1']]
df_TKA = df.ix[df.CCSGroup_Label_CPT_1=='Arthroplasty knee', ['id','NRS','CCSGroup_Label_CPT_1']]
df_SpineFusion = df.ix[df.CCSGroup_Label_CPT_1=='Spinal fusion', ['id','NRS','CCSGroup_Label_CPT_1']]
df_Hysterectomy = df.ix[df.CCSGroup_Label_CPT_1=='Hysterectomy, abdominal and vaginal', ['id','NRS','CCSGroup_Label_CPT_1']]
df_HeartValve = df.ix[df.CCSGroup_Label_CPT_1=='Heart valve procedures', ['id','NRS','CCSGroup_Label_CPT_1']]
CCSGroup_Label_CPT_1
Hip replacement, total and partial                  299
Incision and excision of CNS                        292
Arthroplasty knee                                   281
Other OR therapeutic nervous system procedures      274
No procedure                                        270
Laminectomy, excision intervertebral disc           261
Exploratory laparotomy                              242
Heart valve procedures                              227
Spinal fusion                                       226
Treatment, fracture or dislocation of hip and fe    221
Treatment, fracture or dislocation of lower extr    214
Skin graft                                          209
Hysterectomy, abdominal and vaginal                 200
Aortic resection, replacement or anastomosis        190
Debridement of wound, infection or burn             167
Other OR gastrointestinal therapeutic procedures    159
Nephrectomy, partial or complete                    151
Colorectal resection                                148
Arthroplasty other than hip or knee                 140
Insertion, replacement, or removal of extracrani    140
dtype: int64

Functions to Create Transition Matrices

In each approach,we first create an empty matrix, named counts, where the numbers of rows and columns are indicated by the variable nos. This empty matrix is the framework for the future transition matrix.

The number of patients (num_pts) are given by the number of unique subject ID's. The subject ID, sid, is then used as an indexing variable so that transition counts are performed only within each subject.

A "for" loop is then used with iterrows() to update the counts of the empty matrix each time a subject transitions from one pain state (previousPain) to the next row's pain state (currentPain) using the function counts[previousPain, currentPain] +=1. The row variable is then updated in the "for" loop as the previousRow, and the sid updated as the previousId to step through the loop.

Once the counts matrix is updated with all counts from the "for" loop, it is then normalized to a transition matrix TM. TM is then formatted and indexed to the names of the pain states, and is converted to both a matrix as well as a dataframe for future use later in this notebook. Note that these create both a pandas dataframe as well as an ndarray, and reorganize the array so that state '11' which represented 'asleep' is moved to the beginning, adjacent to '0' to help with visualization.

Two separate functions were created to handle different types of input formats:

1. CSV_to_TM imports a .csv file and transforms this into a transition matrix
2. DF_to_TM imports a df that has already been manually imported and cleaned, as above, and transforms into a transition matrix. This is very helpful for the stratifications.
In [16]:
def CSV_to_TM(d, nos, sid, nrs):
    df = pd.read_csv(d)
    df = df[df.Pain_Scale != 'Off unit']
    df = df.dropna(subset=['Pain_Scale'])
    df['NRS'] = df['Pain_Scale']
    df['NRS'].replace(['Asleep','asleep'],'11', inplace=True)
    df[['id','NRS']]=df[['id','NRS']].astype(int)
    
    counts = np.zeros(shape=(nos,nos))
    num_pts = len(df['id'].unique())
    previousId = 0;
    #iterate over all the rows
    for index, row in df.iterrows():
        if index > 0:
            if previousId == row[sid]:
                previousPain = previousRow[nrs]
#             print 'previous pain score for patient %d is %d' % (row[sid], previousPain)
                currentPain = row[nrs]
#             print 'current pain score for patient %d is %d' % (row[sid], currentPain)
                counts[previousPain, currentPain] +=1            
        previousRow = row
        previousId = row[sid]

    # Counts manually validated on 4-patient sample on 11/714 by PT
    # Create TM from counts
    TM = normalize(counts, axis=1, norm='l1')
    TMdf = pd.DataFrame(TM,index=['0','1','2','3','4','5','6','7','8','9','10','Asleep'], columns=['0','1','2','3','4','5','6','7','8','9','10','Asleep'])
    newindex = ['Asleep','0','1','2','3','4','5','6','7','8','9','10']
    TMdf = TMdf.reindex(newindex)
    cols=TMdf.columns.tolist()
    cols = cols[-1:] + cols[:-1]
    TMdf = TMdf[cols]
    TMa = TMdf.values
    return TMdf, TMa
In [17]:
def DF_to_TM(dataframe, nos, sid, nrs):
    df = dataframe
    counts = np.zeros(shape=(nos,nos))
    num_pts = len(df['id'].unique())
    previousId = 0;
    #iterate over all the rows
    for index, row in df.iterrows():
        if index > 0:
            if previousId == row[sid]:
                previousPain = previousRow[nrs]
                # print 'previous pain score for patient %d is %d' % (row[sid], previousPain)
                currentPain = row[nrs]
                # print 'current pain score for patient %d is %d' % (row[sid], currentPain)
                counts[previousPain, currentPain] +=1            
        previousRow = row
        previousId = row[sid]

    # Counts manually validated on 4-patient sample on 11/7/14 by PT
    # Create TM from counts
    TM = normalize(counts, axis=1, norm='l1')
    TMdf = pd.DataFrame(TM,index=['0','1','2','3','4','5','6','7','8','9','10','Asleep'], columns=['0','1','2','3','4','5','6','7','8','9','10','Asleep'])
    newindex = ['Asleep','0','1','2','3','4','5','6','7','8','9','10']
    TMdf = TMdf.reindex(newindex)
    cols=TMdf.columns.tolist()
    cols = cols[-1:] + cols[:-1]
    TMdf = TMdf[cols]
    TMa = TMdf.values
    return TMdf, TMa

We now test the resulting transition matrices.

In [18]:
Q, Qa = DF_to_TM(df,12,'id','NRS')
print Q
print Qa
          Asleep         0         1         2         3         4         5  \
Asleep  0.411019  0.192805  0.010021  0.032512  0.036865  0.039510  0.059417   
0       0.071548  0.688148  0.012042  0.027180  0.029244  0.032219  0.042669   
1       0.080240  0.256817  0.289249  0.113153  0.065465  0.055015  0.055135   
2       0.090482  0.190777  0.044409  0.289243  0.095638  0.076386  0.072768   
3       0.089249  0.166043  0.026143  0.087687  0.262082  0.095603  0.095639   
4       0.098056  0.142274  0.019274  0.071863  0.092588  0.243199  0.111861   
5       0.112834  0.125126  0.015581  0.057568  0.075096  0.085441  0.261888   
6       0.114426  0.101514  0.008937  0.047195  0.062365  0.082019  0.114362   
7       0.116942  0.080514  0.005797  0.031272  0.049028  0.063185  0.101779   
8       0.128545  0.071758  0.004792  0.023810  0.035805  0.050031  0.077056   
9       0.121436  0.051607  0.003756  0.015718  0.026777  0.034288  0.058631   
10      0.123871  0.053190  0.003123  0.014943  0.024121  0.028349  0.051749   

               6         7         8         9        10  
Asleep  0.048156  0.052388  0.058646  0.024426  0.034235  
0       0.028513  0.025345  0.024062  0.007655  0.011375  
1       0.029670  0.022823  0.019219  0.007087  0.006126  
2       0.051312  0.036218  0.031145  0.009065  0.012558  
3       0.061545  0.051414  0.038089  0.011401  0.015105  
4       0.078917  0.060656  0.046614  0.015966  0.018734  
5       0.090348  0.073680  0.058908  0.019400  0.024130  
6       0.231555  0.103613  0.080938  0.024711  0.028368  
7       0.102663  0.262928  0.110657  0.037557  0.037679  
8       0.085806  0.116581  0.289205  0.060508  0.056103  
9       0.063430  0.099597  0.149882  0.279316  0.095563  
10      0.045166  0.067317  0.111522  0.077888  0.398760  
[[ 0.411  0.193  0.01   0.033  0.037  0.04   0.059  0.048  0.052  0.059  0.024  0.034]
 [ 0.072  0.688  0.012  0.027  0.029  0.032  0.043  0.029  0.025  0.024  0.008  0.011]
 [ 0.08   0.257  0.289  0.113  0.065  0.055  0.055  0.03   0.023  0.019  0.007  0.006]
 [ 0.09   0.191  0.044  0.289  0.096  0.076  0.073  0.051  0.036  0.031  0.009  0.013]
 [ 0.089  0.166  0.026  0.088  0.262  0.096  0.096  0.062  0.051  0.038  0.011  0.015]
 [ 0.098  0.142  0.019  0.072  0.093  0.243  0.112  0.079  0.061  0.047  0.016  0.019]
 [ 0.113  0.125  0.016  0.058  0.075  0.085  0.262  0.09   0.074  0.059  0.019  0.024]
 [ 0.114  0.102  0.009  0.047  0.062  0.082  0.114  0.232  0.104  0.081  0.025  0.028]
 [ 0.117  0.081  0.006  0.031  0.049  0.063  0.102  0.103  0.263  0.111  0.038  0.038]
 [ 0.129  0.072  0.005  0.024  0.036  0.05   0.077  0.086  0.117  0.289  0.061  0.056]
 [ 0.121  0.052  0.004  0.016  0.027  0.034  0.059  0.063  0.1    0.15   0.279  0.096]
 [ 0.124  0.053  0.003  0.015  0.024  0.028  0.052  0.045  0.067  0.112  0.078  0.399]]

Determine max and min values within the arrays.

In [19]:
print np.amax(Qa)
print np.where(Qa==Qa.max())
print np.amin(Qa)
print np.where(Qa==Qa.min())
0.688148227789
(array([1]), array([1]))
0.00312319815491
(array([11]), array([2]))

Visualize Transition Matrices

Here, we use the seaborn library to aid with visualization of the transition matrices, converting them into the Figures used throughout the manuscript.

In [20]:
# print Qa
sns.set(context = 'poster')
sns.axlabel(xlabel='Following State j', ylabel = 'Initial State i')
plt.title('Transition Matrix of Postoperative Acute Pain States', weight='bold')
plt.yticks(rotation=0)
sns.heatmap(Q, annot=True, linewidths=0.5)
plt.savefig("Pain MC Figure 1.pdf")

Simulation of MC

In [25]:
#Review Arrays
print Q
print Qa
          Asleep         0         1         2         3         4         5  \
Asleep  0.411019  0.192805  0.010021  0.032512  0.036865  0.039510  0.059417   
0       0.071548  0.688148  0.012042  0.027180  0.029244  0.032219  0.042669   
1       0.080240  0.256817  0.289249  0.113153  0.065465  0.055015  0.055135   
2       0.090482  0.190777  0.044409  0.289243  0.095638  0.076386  0.072768   
3       0.089249  0.166043  0.026143  0.087687  0.262082  0.095603  0.095639   
4       0.098056  0.142274  0.019274  0.071863  0.092588  0.243199  0.111861   
5       0.112834  0.125126  0.015581  0.057568  0.075096  0.085441  0.261888   
6       0.114426  0.101514  0.008937  0.047195  0.062365  0.082019  0.114362   
7       0.116942  0.080514  0.005797  0.031272  0.049028  0.063185  0.101779   
8       0.128545  0.071758  0.004792  0.023810  0.035805  0.050031  0.077056   
9       0.121436  0.051607  0.003756  0.015718  0.026777  0.034288  0.058631   
10      0.123871  0.053190  0.003123  0.014943  0.024121  0.028349  0.051749   

               6         7         8         9        10  
Asleep  0.048156  0.052388  0.058646  0.024426  0.034235  
0       0.028513  0.025345  0.024062  0.007655  0.011375  
1       0.029670  0.022823  0.019219  0.007087  0.006126  
2       0.051312  0.036218  0.031145  0.009065  0.012558  
3       0.061545  0.051414  0.038089  0.011401  0.015105  
4       0.078917  0.060656  0.046614  0.015966  0.018734  
5       0.090348  0.073680  0.058908  0.019400  0.024130  
6       0.231555  0.103613  0.080938  0.024711  0.028368  
7       0.102663  0.262928  0.110657  0.037557  0.037679  
8       0.085806  0.116581  0.289205  0.060508  0.056103  
9       0.063430  0.099597  0.149882  0.279316  0.095563  
10      0.045166  0.067317  0.111522  0.077888  0.398760  
[[ 0.411  0.193  0.01   0.033  0.037  0.04   0.059  0.048  0.052  0.059  0.024  0.034]
 [ 0.072  0.688  0.012  0.027  0.029  0.032  0.043  0.029  0.025  0.024  0.008  0.011]
 [ 0.08   0.257  0.289  0.113  0.065  0.055  0.055  0.03   0.023  0.019  0.007  0.006]
 [ 0.09   0.191  0.044  0.289  0.096  0.076  0.073  0.051  0.036  0.031  0.009  0.013]
 [ 0.089  0.166  0.026  0.088  0.262  0.096  0.096  0.062  0.051  0.038  0.011  0.015]
 [ 0.098  0.142  0.019  0.072  0.093  0.243  0.112  0.079  0.061  0.047  0.016  0.019]
 [ 0.113  0.125  0.016  0.058  0.075  0.085  0.262  0.09   0.074  0.059  0.019  0.024]
 [ 0.114  0.102  0.009  0.047  0.062  0.082  0.114  0.232  0.104  0.081  0.025  0.028]
 [ 0.117  0.081  0.006  0.031  0.049  0.063  0.102  0.103  0.263  0.111  0.038  0.038]
 [ 0.129  0.072  0.005  0.024  0.036  0.05   0.077  0.086  0.117  0.289  0.061  0.056]
 [ 0.121  0.052  0.004  0.016  0.027  0.034  0.059  0.063  0.1    0.15   0.279  0.096]
 [ 0.124  0.053  0.003  0.015  0.024  0.028  0.052  0.045  0.067  0.112  0.078  0.399]]

Create and Demonstrate Simulation over Sequential Transitions through Markov Chain given an Initial State

Here, we offer several simulations of the Markov Chain, where we examine the effect of multiplying the Markov Chain by itself to determine how the pain state will probabilistically change over sequential states.

The function Simulate uses the variables listed in the commented section (denoted by the # preceding each line), which are manually input by the user and are intended to offer a range of initial conditions. The random.seed permits "stabilization" of an otherwise random number generator to permit repeatability of the findings.

For each iteration, the initial state is first specified by the user using the init variable. The "for" loops then iterate to the next state using the probabilities from the transition matrices corresponding to the given initial state. This is repeated by the number of iterations manually entered as the variable T. This entire process is then replicated for several simulated virtual subjects, the number of which is given by the variable r.

In [22]:
#Create simulation over time through Markov Chain
#Q: array (Qa here is an example)
#T: number of iterations
#seed: random number seed
#r: number of different virtual subjects
#init: initial pain state state, can be random or specifically entered

def Simulate(Q,T,seed,r, init, initlabel ) :
    P=Q.copy()
    n = len(P)
    random.seed(seed)
#     sns.set(context="talk", style="whitegrid", font_scale=1.2)
    sns.set_style("whitegrid", {'axes.axisbelow': True,"grid.linewidth": 1,'axes.labelcolor': '0','text.color': '0', 'grid.color': '.9','grid.linestyle': '-','axes.linewidth': 2,"font_scale":6,
                                'axes.edgecolor': '.2'})
    plt.figure(figsize=(20, 10), dpi=300)
    for i in range(n):
        for j in range(n-1):
            P[i,j+1]= P[i,j]+Q[i,j+1]
    for x in range(r) :
#         i = random.randint(0,n-1)
        i = init
        S=[]
        Time = []
        S.append(i)
        Time.append(0)
        for t in range(T) :
            u = random.random()
            j=0
            while u > P[i,j] :
                j = j+1
            S.append(j)
            Time.append(t+1)
            i=j
        plt.plot(Time,S, linewidth=6.0)
        plt.title('Simulation of Markov Chain Iterations from Initial Pain State %s' % initlabel)
        plt.xlabel('Number of Transitions')
        plt.ylabel('Pain Intensity')
        plt.yticks([0,1,3,5,7,9,11],['Asleep','0','2','4','6','8','10'])
        plt.savefig('Figure 2 MCSimulation Initial State %s.pdf' % initlabel , dpi=600)
    plt.show()
    return S


Simulate(Qa, 10, 7, 5, 0, 'Asleep')
Simulate(Qa, 10, 7, 5, 1, '0')
Simulate(Qa, 10, 7, 5, 6, '5')
Simulate(Qa, 10, 7, 5, 11, '10')
Out[22]:
[11, 9, 5, 7, 7, 3, 3, 3, 7, 7, 4]

Create separate transition matrices for each stratification level.

In [23]:
#Sex
Q_male, Qa_male = DF_to_TM(df_male,12,'id','NRS')
Q_female, Qa_female = DF_to_TM(df_female,12,'id','NRS')

#Age
Q_21_39, Qa_21_39 = DF_to_TM(df_21_39,12,'id','NRS')
Q_40_64, Qa_40_64 = DF_to_TM(df_40_64,12,'id','NRS')
Q_65_84, Qa_65_84 = DF_to_TM(df_65_84,12,'id','NRS')
Q_85_orGreater, Qa_85_orGreater = DF_to_TM(df_85_orGreater,12,'id','NRS')

#Type of surgery
Q_THA, Qa_THA = DF_to_TM(df_THA,12,'id','NRS')
Q_TKA, Qa_TKA = DF_to_TM(df_TKA,12,'id','NRS')
Q_SpineFusion, Qa_SpineFusion = DF_to_TM(df_SpineFusion,12,'id','NRS')
Q_Hysterectomy, Qa_Hysterectomy = DF_to_TM(df_Hysterectomy,12,'id','NRS')
Q_HeartValve, Qa_HeartValve = DF_to_TM(df_HeartValve,12,'id','NRS')

#Note: Verified against Bzdega coded stratifications using sampling for male, 21_39, and TKA.

Identify max values for each stratification level.

In [24]:
#Sex
print 'Sex for male & female'
print np.amax(Qa_male); print np.where(Qa_male==Qa_male.max())
print np.amax(Qa_female); print np.where(Qa_female == Qa_female.max()); print;

#Age
print "By age group:"
print np.amax(Qa_21_39); print np.where(Qa_21_39==Qa_21_39.max())
print np.amax(Qa_40_64); print np.where(Qa_40_64==Qa_40_64.max())
print np.amax(Qa_65_84); print np.where(Qa_65_84==Qa_65_84.max())
print np.amax(Qa_85_orGreater); print np.where(Qa_85_orGreater==Qa_85_orGreater.max()); print;

#Type of surgery
print "By type of surgery:"
print np.amax(Qa_THA); print np.where(Qa_THA==Qa_THA.max())
print np.amax(Qa_TKA); print np.where(Qa_TKA==Qa_TKA.max())
print np.amax(Qa_SpineFusion); print np.where(Qa_SpineFusion==Qa_SpineFusion.max())
print np.amax(Qa_Hysterectomy); print np.where(Qa_Hysterectomy==Qa_Hysterectomy.max())
print np.amax(Qa_HeartValve); print np.where(Qa_HeartValve==Qa_HeartValve.max());print;
Sex for male & female
0.706724133441
(array([1]), array([1]))
0.66540172521
(array([1]), array([1]))

By age group:
0.585522828803
(array([1]), array([1]))
0.651431747682
(array([1]), array([1]))
0.733251924444
(array([1]), array([1]))
0.771179188773
(array([1]), array([1]))

By type of surgery:
0.68059490085
(array([1]), array([1]))
0.65413761746
(array([1]), array([1]))
0.621717990276
(array([1]), array([1]))
0.473126419379
(array([1]), array([1]))
0.763272914256
(array([1]), array([1]))

Identify min values for each stratification level.

In [25]:
#Sex
print 'Sex for male & female'
print np.amin(Qa_male); print np.where(Qa_male==Qa_male.min())
print np.amin(Qa_female); print np.where(Qa_female == Qa_female.min()); print;

#Age
print "By age group:"
print np.amin(Qa_21_39); print np.where(Qa_21_39==Qa_21_39.min())
print np.amin(Qa_40_64); print np.where(Qa_40_64==Qa_40_64.min())
print np.amin(Qa_65_84); print np.where(Qa_65_84==Qa_65_84.min())
print np.amin(Qa_85_orGreater); print np.where(Qa_85_orGreater==Qa_85_orGreater.min()); print;

#Type of surgery
print "By type of surgery:"
print np.amin(Qa_THA); print np.where(Qa_THA==Qa_THA.min())
print np.amin(Qa_TKA); print np.where(Qa_TKA==Qa_TKA.min())
print np.amin(Qa_SpineFusion); print np.where(Qa_SpineFusion==Qa_SpineFusion.min())
print np.amin(Qa_Hysterectomy); print np.where(Qa_Hysterectomy==Qa_Hysterectomy.min())
print np.amin(Qa_HeartValve); print np.where(Qa_HeartValve==Qa_HeartValve.min());print;
Sex for male & female
0.00345500976416
(array([10]), array([2]))
0.00271911538113
(array([11]), array([2]))

By age group:
0.00210837022981
(array([11]), array([2]))
0.00271239828506
(array([11]), array([2]))
0.00416294974725
(array([2]), array([10]))
0.0
(array([ 2, 10]), array([10,  2]))

By type of surgery:
0.00350262697023
(array([10]), array([2]))
0.0
(array([2]), array([11]))
0.00140449438202
(array([11]), array([2]))
0.0
(array([ 2,  3, 11]), array([11, 11,  2]))
0.00153139356815
(array([2, 2]), array([10, 11]))

Visualize transition matrices via heat map for each stratification

In [26]:
#Male TM
sns.set(context='poster')
sns.heatmap(Q_male, annot=True, linewidths=0.5, square=True)
sns.axlabel(xlabel='Following State j', ylabel = 'Initial State i')
plt.yticks(rotation=0)
plt.title('Transition Matrix of Male Subjects', weight='bold')
plt.savefig("Pain MC Figure 3 Male.pdf")
In [27]:
sns.set(context='poster')
sns.heatmap(Q_female, annot=True, linewidths=0.5, square=True)
sns.axlabel(xlabel='Following State j', ylabel = 'Initial State i')
plt.yticks(rotation=0)
plt.title('Transition Matrix of Female Subjects', weight='bold')
plt.savefig("Pain MC Figure 3 Female.pdf")
In [28]:
sns.set(context='poster')
sns.heatmap(Q_21_39, annot=True, linewidths=0.5, square=True)
sns.axlabel(xlabel='Following State j', ylabel = 'Initial State i')
plt.yticks(rotation=0)
plt.title('Transition Matrix of Subjects Age 21-39', weight='bold')
plt.savefig("Pain MC Figure 3 Age 21-39.pdf")
In [29]:
sns.set(context='poster')
sns.heatmap(Q_40_64, annot=True, linewidths=0.5, square=True)
sns.axlabel(xlabel='Following State j', ylabel = 'Initial State i')
plt.yticks(rotation=0)
plt.title('Transition Matrix of Subjects Age 40-64', weight='bold')
plt.savefig("Pain MC Figure 3 Age 40-64.pdf")
In [30]:
sns.set(context='poster')
sns.heatmap(Q_65_84, annot=True, linewidths=0.5, square=True)
sns.axlabel(xlabel='Following State j', ylabel = 'Initial State i')
plt.yticks(rotation=0)
plt.title('Transition Matrix of Subjects Age 65-84', weight='bold')
plt.savefig("Pain MC Figure 3 Age 65-84.pdf")
In [31]:
sns.set(context='poster')
sns.heatmap(Q_85_orGreater, annot=True, linewidths=0.5, square=True)
sns.axlabel(xlabel='Following State j', ylabel = 'Initial State i')
plt.yticks(rotation=0)
plt.title('Transition Matrix of Subjects Age 85 or Greater', weight='bold')
plt.savefig("Pain MC Figure 3 Age 85 or Greater.pdf")
In [32]:
sns.set(context='poster')
sns.heatmap(Q_THA, annot=True, linewidths=0.5, square=True)
sns.axlabel(xlabel='Following State j', ylabel = 'Initial State i')
plt.yticks(rotation=0)
plt.title('Transition Matrix of Subjects Following THA', weight='bold')
plt.savefig("Pain MC Figure 3 THA.pdf")
In [33]:
sns.set(context='poster')
sns.heatmap(Q_TKA, annot=True, linewidths=0.5, square=True)
sns.axlabel(xlabel='Following State j', ylabel = 'Initial State i')
plt.yticks(rotation=0)
plt.title('Transition Matrix of Subjects Following TKA', weight='bold')
plt.savefig("Pain MC Figure 3 TKA.pdf")
In [34]:
sns.set(context='poster')
sns.heatmap(Q_SpineFusion, annot=True, linewidths=0.5, square=True)
sns.axlabel(xlabel='Following State j', ylabel = 'Initial State i')
plt.yticks(rotation=0)
plt.title('Transition Matrix of Subjects Following Spine Fusion', weight='bold')
plt.savefig("Pain MC Figure 3 Spine Fusion.pdf")
In [35]:
sns.set(context='poster')
sns.heatmap(Q_Hysterectomy, annot=True, linewidths=0.5, square=True)
sns.axlabel(xlabel='Following State j', ylabel = 'Initial State i')
plt.yticks(rotation=0)
plt.title('Transition Matrix of Subjects Following Hysterectomy', weight='bold')
plt.savefig("Pain MC Figure 3 Hysterectomy.pdf")
In [36]:
sns.set(context='poster')
sns.heatmap(Q_HeartValve, annot=True, linewidths=0.5, square=True)
sns.axlabel(xlabel='Following State j', ylabel = 'Initial State i')
plt.yticks(rotation=0)
plt.title('Transition Matrix of Subjects Following Heart Valve Surgery', weight='bold')
plt.savefig("Pain MC Figure 3 Heart Valve Surgery.pdf")

Stationary Distribution of Transition Matrices

Provides stationary distribution of matrix using lambda=1's eigenvector method.

Please see comments embedded within the function for Pi for a description of how the stationary distribution is calculated using this method.

In [37]:
def Pi(a) :
    Q = np.asmatrix(a, float) #uses arrays instead of matrix for input
    Qt = np.matrix.transpose(Q) #Creates matrix transpose
    l,v = la.eig(Qt) #Eigenvalue, Eigenvector from Qt
    n = len(l) #Number of values in array of eigenvalues l
    inx = 0 #Initializes inx
    for i in range(n):
        if l[i] == 1.0:
            inx = i          
            break  #this looks for the index inx for the Eigenvalue of 1 within the list of Eigenvalues l
    vt = np.matrix.transpose(v) #transpose of v 
    t = vt[inx] #finds Eigenvector in vt associated with lambda of 1; this is the stationary distribution
    #We now have t, which is a matrix with a single row of values
    #and each column refers to a given pain state
    p = scipy.zeros(n)
    sum=0
    for i in range(n): #this creates a denominator for probabilities of the stationary distribution
        p[i] = t[0,i]
        sum = sum + p[i]
    
    for i in range(n):
        p[i]= p[i]/sum #this creates the probabilities of the stationary distribution via normalization of the eigenvector t
    return p

Stratification of stationary distributions

In [38]:
print 'Stationary Distributions of Postoperative Pain State Transition Matrices'
print list(Q)
print
print 'All subjects:'
Qa_stationary = Pi(Qa); print Qa_stationary
print 'By Sex:'
Qa_male_stationary = Pi(Qa_male); print Qa_male_stationary, 'Male'
Qa_female_stationary = Pi(Qa_female); print Qa_female_stationary, 'Female'
print 'By Age Group:'
Qa_21_39_stationary = Pi(Qa_21_39); print Qa_21_39_stationary, '21-39 years'
Qa_40_64_stationary = Pi(Qa_40_64); print Qa_40_64_stationary, '40-64 years'
Qa_65_84_stationary = Pi(Qa_65_84); print Qa_65_84_stationary, '65-84 years'
Qa_85_orGreater_stationary = Pi(Qa_85_orGreater); print Qa_85_orGreater_stationary, '>= 85 years'
print 'By Type of Surgery:'
Qa_THA_stationary = Pi(Qa_THA); print Qa_THA_stationary, 'THA'
Qa_TKA_stationary = Pi(Qa_TKA); print Qa_TKA_stationary, 'TKA'
Qa_SpineFusion_stationary = Pi(Qa_SpineFusion); print Qa_SpineFusion_stationary, 'Spinal Fusion'
Qa_Hysterectomy_stationary = Pi(Qa_Hysterectomy);print Qa_Hysterectomy_stationary, 'Hysterectomy'
Qa_HeartValve_stationary = Pi(Qa_HeartValve);print Qa_HeartValve_stationary, 'Heart Valve'
Stationary Distributions of Postoperative Pain State Transition Matrices
['Asleep', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '10']

All subjects:
[ 0.14   0.299  0.018  0.053  0.06   0.065  0.086  0.068  0.07   0.07   0.03   0.041]
By Sex:
[ 0.146  0.316  0.019  0.055  0.061  0.063  0.083  0.065  0.066  0.064  0.027  0.037] Male
[ 0.134  0.28   0.018  0.051  0.06   0.066  0.089  0.071  0.075  0.078  0.033  0.045] Female
By Age Group:
[ 0.124  0.181  0.014  0.049  0.061  0.073  0.103  0.09   0.1    0.1    0.045  0.059] 21-39 years
[ 0.134  0.249  0.018  0.053  0.064  0.069  0.093  0.075  0.081  0.082  0.036  0.047] 40-64 years
[ 0.154  0.407  0.022  0.056  0.057  0.057  0.07   0.049  0.044  0.043  0.016  0.026] 65-84 years
[ 0.181  0.51   0.014  0.038  0.034  0.036  0.047  0.038  0.034  0.036  0.011  0.019] >= 85 years
By Type of Surgery:
[ 0.084  0.245  0.041  0.089  0.08   0.08   0.096  0.074  0.076  0.07   0.033  0.033] THA
[ 0.072  0.194  0.04   0.092  0.095  0.092  0.112  0.082  0.086  0.076  0.029  0.031] TKA
[ 0.11   0.197  0.012  0.051  0.063  0.067  0.105  0.079  0.083  0.103  0.046  0.084] Spinal Fusion
[ 0.065  0.228  0.028  0.071  0.085  0.101  0.116  0.085  0.076  0.08   0.03   0.034] Hysterectomy
[ 0.192  0.457  0.019  0.051  0.054  0.049  0.056  0.036  0.033  0.031  0.009  0.014] Heart Valve
-c:18: ComplexWarning: Casting complex values to real discards the imaginary part

AD and KS Testing for Comparison of Stationary Distributions

This code applies the Anderson-Darling (AD) and Kolmogorov-Smirnov (KS) test for similarity amongst multiple probability distributions. The initial arrays were copied from the above cell's stationary distributions for each stratification level. The AD and KS tests were called for directly using the scipy.stats framework.

In [39]:
Male_stat= np.asarray([0.146,0.316,0.019,0.055,0.061,0.063,0.083,0.065,0.066,0.064,0.027,0.037])
Female_stat=np.asarray([0.134,0.28, 0.018,0.051,0.06, 0.066,0.089,0.071,0.075,0.078,0.033,0.045])

a2139_years_stat = [0.124,0.181,0.014,0.049,0.061,0.073,0.103,0.09, 0.1,0.1,0.045,0.059]
a4064_years_stat = [0.134,0.249,0.018,0.053,0.064,0.069,0.093,0.075,0.081,0.082,0.036,0.047]
a6584_years_stat = [0.154,0.407,0.022,0.056,0.057,0.057,0.07, 0.049,0.044,0.043,0.016,0.026]
aGte85_years_stat = [0.181,0.51, 0.014,0.038,0.034,0.036,0.047,0.038,0.034,0.036,0.011,0.019]

THA_stat = [ 0.084,0.245,0.041,0.089,0.08, 0.08, 0.096,0.074,0.076,0.07, 0.033,0.033] 
TKA_stat = [ 0.072,0.194,0.04, 0.092,0.095,0.092,0.112,0.082,0.086,0.076,0.029,0.031] 
SpinalFusion_stat = [ 0.11, 0.197,0.012,0.051,0.063,0.067,0.105,0.079,0.083,0.103,0.046,0.084] 
Hysterectomy_stat = [ 0.065,0.228,0.028,0.071,0.085,0.101,0.116,0.085,0.076,0.08, 0.03, 0.034] 
HeartValve_stat = [ 0.192,0.457,0.019,0.051,0.054,0.049,0.056,0.036,0.033,0.031,0.009,0.014] 


print 'Omnibus Tests using Anderson-Darling:'
print 'Legend: AD statistic, threshold values, p-value'
print '(null hypothesis = samples from same distribution)';print;
print 'AD Test for Differences in Stationary Distribution by Sex: '
print anderson_ksamp([Male_stat, Female_stat]); print;
print 'AD Test for Differences in Stationary Distribution by Age: '
print anderson_ksamp([a2139_years_stat, a4064_years_stat, a6584_years_stat,aGte85_years_stat]); print;
print 'AD Test for Differences in Stationary Distribution by Type of Surgery: '
print anderson_ksamp([THA_stat,TKA_stat,SpinalFusion_stat,Hysterectomy_stat,HeartValve_stat]); print;
print;
print '2-Sample KS Tests for Pairs of Age Groups:'
print '21-39 vs 21-39 Test of Equivalency: ', ks_2samp(a2139_years_stat, a2139_years_stat)
print;
print '21-39 vs 40-64: ', ks_2samp(a2139_years_stat, a4064_years_stat)
print '21-39 vs 65-84: ', ks_2samp(a2139_years_stat, a6584_years_stat)
print '21-39 vs GTE85: ', ks_2samp(a2139_years_stat, aGte85_years_stat)
print;
print '40-64 vs 65-84: ', ks_2samp(a4064_years_stat, a6584_years_stat)
print '40-64 vs GTE85: ', ks_2samp(a4064_years_stat, aGte85_years_stat)
print;
print '65-84 vs GTE85: ', ks_2samp(a6584_years_stat, aGte85_years_stat)
print;
print '2-Sample KS Tests for Pairs of Surgery Types:'
print 'THA vs THA Test of Equivalency: ', ks_2samp(THA_stat, THA_stat)
print;
print 'THA vs TKA: ', ks_2samp(THA_stat, TKA_stat)
print 'THA vs Spinal Fusion: ', ks_2samp(THA_stat, SpinalFusion_stat)
print 'THA vs Hysterectomy: ', ks_2samp(THA_stat, Hysterectomy_stat)
print 'THA vs Heart Valve: ', ks_2samp(THA_stat, HeartValve_stat)
print;
print 'TKA vs Spinal Fusion: ', ks_2samp(TKA_stat, SpinalFusion_stat)
print 'TKA vs Hysterectomy: ', ks_2samp(TKA_stat, Hysterectomy_stat)
print 'TKA vs Heart Valve: ', ks_2samp(TKA_stat, HeartValve_stat)
print;
print 'Spinal Fusion vs Hysterectomy: ', ks_2samp(SpinalFusion_stat, Hysterectomy_stat)
print 'Spinal Fusion vs Heart Valve: ', ks_2samp(SpinalFusion_stat, HeartValve_stat)
print;
print 'Hysterectomy: vs Heart Valve: ', ks_2samp(Hysterectomy_stat, HeartValve_stat)
Omnibus Tests using Anderson-Darling:
Legend: AD statistic, threshold values, p-value
(null hypothesis = samples from same distribution)

AD Test for Differences in Stationary Distribution by Sex: 
(-0.84853999331274155, array([ 0.325,  1.226,  1.961,  2.718,  3.752]), 0.8570331715343842)

AD Test for Differences in Stationary Distribution by Age: 
(2.4619996867557692, array([ 0.499,  1.324,  1.916,  2.493,  3.246]), 0.026103754246365633)

AD Test for Differences in Stationary Distribution by Type of Surgery: 
(0.68290848452577224, array([ 0.526,  1.33 ,  1.893,  2.437,  3.138]), 0.21023751727990872)


2-Sample KS Tests for Pairs of Age Groups:
21-39 vs 21-39 Test of Equivalency:  (0.0, 1.0)

21-39 vs 40-64:  (0.25, 0.78641716217514468)
21-39 vs 65-84:  (0.5, 0.065583963918802238)
21-39 vs GTE85:  (0.66666666666666674, 0.0045964438460830122)

40-64 vs 65-84:  (0.41666666666666669, 0.186196839004176)
40-64 vs GTE85:  (0.58333333333333337, 0.019091732631329447)

65-84 vs GTE85:  (0.5, 0.065583963918802238)

2-Sample KS Tests for Pairs of Surgery Types:
THA vs THA Test of Equivalency:  (0.0, 1.0)

THA vs TKA:  (0.25, 0.78641716217514468)
THA vs Spinal Fusion:  (0.25, 0.78641716217514468)
THA vs Hysterectomy:  (0.16666666666666666, 0.99133252540492089)
THA vs Heart Valve:  (0.58333333333333337, 0.019091732631329447)

TKA vs Spinal Fusion:  (0.16666666666666674, 0.99133252540492101)
TKA vs Hysterectomy:  (0.25, 0.78641716217514468)
TKA vs Heart Valve:  (0.58333333333333337, 0.019091732631329447)

Spinal Fusion vs Hysterectomy:  (0.16666666666666674, 0.99133252540492101)
Spinal Fusion vs Heart Valve:  (0.58333333333333337, 0.019091732631329447)

Hysterectomy: vs Heart Valve:  (0.58333333333333337, 0.019091732631329447)
/Users/ptighe/anaconda/lib/python2.7/site-packages/scipy/stats/morestats.py:1351: UserWarning: approximate p-value will be computed by extrapolation
  warnings.warn("approximate p-value will be computed by extrapolation")

Correct for multiple comparisons

We then use the statsmodel's multitest approach to control for the effect of multiple comparison's of the above stationary distributions. The p-values are taken from the above cell.

In [40]:
import statsmodels.stats.multitest as smm

print 'Age Group p-value HS Adjustment for Multiple comparison: '
print smm.multipletests([0.79,0.66,0.005,0.19,0.02,0.07],alpha=0.05,method='hs')
print;
print 'Type of Surgery p-value HS Adjustment for Multiple comparison: '
print smm.multipletests([0.79,0.79,0.99,0.02,0.99,0.79, 0.02, 0.99, 0.02, 0.02],alpha=0.05,method='hs')
Age Group p-value HS Adjustment for Multiple comparison: 
(array([False, False,  True, False, False, False], dtype=bool), array([ 0.884,  0.884,  0.03 ,  0.469,  0.096,  0.252]), 0.008512444610847103, 0.008333333333333333)

Type of Surgery p-value HS Adjustment for Multiple comparison: 
(array([False, False, False, False, False, False, False, False, False, False], dtype=bool), array([ 1.   ,  1.   ,  1.   ,  0.183,  1.   ,  1.   ,  0.183,  1.   ,  0.183,  0.183]), 0.0051161968918237433, 0.005)

Visualizations of Kernel Densities of Stratified Stationary Distributions

In [41]:
#Kernel Densities of Steady State Distributions Overall
sns.set(context="talk", style="whitegrid", font_scale=1.2)
sns.kdeplot(Qa_stationary, shade=True, legend=True)
sns.axlabel(xlabel='Steady State Probability', ylabel = 'Kernel Density')
In [42]:
#Kernel Densities of Steady State Distributions by Sex
sns.set(context="talk", style="whitegrid", font_scale=1.2)
c1,c2, c3,c4,c5,c6,c7 = sns.color_palette("Set1", 7)
f, ax = plt.subplots(1,1, sharex=True)
ax.set_title('Kernel Densities of Steady State Distributions by Sex', weight='bold')
sns.kdeplot(Qa_male_stationary, shade=True, color=c2, label='Male', legend=True)
sns.kdeplot(Qa_female_stationary, shade=True, color=c1,label='Female', legend=True)
sns.axlabel(xlabel='Steady State Probability', ylabel = 'Kernel Density')
In [43]:
#Kernel Densities of Steady State Distributions by Age Group
sns.set(context="talk", style="whitegrid", font_scale=1.2)
c1,c2, c3,c4,c5,c6,c7 = sns.color_palette("Set1", 7)
f, ax = plt.subplots(1,1, sharex=True)
plt.title('Kernel Densities of Steady State Distributions by Age Group', weight='bold')
# plt.suptitle('21-39:Red   40-64:Blue  65-84:Green  85 or Greater: Purple')
sns.kdeplot(Qa_21_39_stationary, shade=True, color=c1, label='21-39 years', legend=True)
sns.kdeplot(Qa_40_64_stationary, shade=True, color=c2, label='40-64 years', legend=True)
sns.kdeplot(Qa_65_84_stationary, shade=True, color=c3, label='65-84 years', legend=True)
sns.kdeplot(Qa_85_orGreater_stationary, shade=True, color=c4, label='85 or Greater years', legend=True)
sns.axlabel(xlabel='Steady State Probability', ylabel = 'Kernel Density')
plt.legend()
Out[43]:
<matplotlib.legend.Legend at 0x110ab3f90>
In [44]:
#Kernel Densities of Steady State Distributions by Type of Surgery
sns.set(context="talk", style="whitegrid", font_scale=1.2)
c1,c2, c3,c4,c5,c6,c7 = sns.color_palette("Set1", 7)
f, ax = plt.subplots(1,1, sharex=True)
plt.title('Kernel Densities of Steady State Distributions by Surgery', weight='bold')
# plt.suptitle('21-39:Red   40-64:Blue  65-84:Green  85 or Greater: Purple')
sns.kdeplot(Qa_THA_stationary, shade=True, color=c1, label='THA', legend=True)
sns.kdeplot(Qa_TKA_stationary, shade=True, color=c2, label='TKA', legend=True)
sns.kdeplot(Qa_SpineFusion_stationary, shade=True, color=c3, label='Spine Fusion', legend=True)
sns.kdeplot(Qa_Hysterectomy_stationary, shade=True, color=c3, label='Hysterectomy', legend=True)
sns.kdeplot(Qa_HeartValve_stationary, shade=True, color=c4, label='Heart Valve', legend=True)
sns.axlabel(xlabel='Steady State Probability', ylabel = 'Kernel Density')
plt.legend()
Out[44]:
<matplotlib.legend.Legend at 0x10d8d8510>

Bar Charts of Stratified Steady State Distributions

In [45]:
Qs_df=pd.DataFrame(Qa_stationary, index=['Asleep','0','1','2','3','4','5','6','7','8','9','10'],columns=['All Subjects'])
# Qs_df = Qs_df.reindex(newindex)

sns.set(context="talk", style="white", font_scale=1.2)
Qs_df.plot(kind='bar')
sns.axlabel(xlabel='Pain State', ylabel = 'Steady State Probability')
plt.xticks(fontsize=14, rotation=45)
plt.title('Steady State Distributions for All Subjects',weight='bold')
Out[45]:
<matplotlib.text.Text at 0x113ff2c90>
In [51]:
Qms_df=pd.DataFrame(Qa_male_stationary, index=['Asleep','0','1','2','3','4','5','6','7','8','9','10'], columns=['Male'])
Qfs_df=pd.DataFrame(Qa_female_stationary, index=['Asleep','0','1','2','3','4','5','6','7','8','9','10'], columns=['Female'])
# Qms_df = Qms_df.reindex(newindex)
# Qfs_df = Qfs_df.reindex(newindex)
Qs_sex_df = pd.concat([Qms_df,Qfs_df], axis=1)

sns.set(context="talk", style="white", font_scale=1.2)
Qs_sex_df.plot(kind='bar')
sns.axlabel(xlabel='Pain State', ylabel = 'Steady State Probability')
plt.xticks(fontsize=14, rotation=45)
plt.title('Steady State Distributions by Sex',weight='bold')
plt.savefig("Pain MC Figure 4 Sex.pdf")
In [50]:
#Age Group Stationary Distribution Comparison
Q_21_39_stationary_df = pd.DataFrame(Qa_21_39_stationary, index=['Asleep','0','1','2','3','4','5','6','7','8','9','10'], columns=['21-39 years'])
Q_40_64_stationary_df = pd.DataFrame(Qa_40_64_stationary, index=['Asleep','0','1','2','3','4','5','6','7','8','9','10'], columns=['40-64 years'])
Q_65_84_stationary_df = pd.DataFrame(Qa_65_84_stationary, index=['Asleep','0','1','2','3','4','5','6','7','8','9','10'], columns=['65-84 years'])
Q_85_Gr_stationary_df = pd.DataFrame(Qa_85_orGreater_stationary, index=['Asleep','0','1','2','3','4','5','6','7','8','9','10'], columns=['85 and Greater years'])

Qs_age_df = pd.concat([Q_21_39_stationary_df,Q_40_64_stationary_df,Q_65_84_stationary_df,Q_85_Gr_stationary_df], axis=1)

sns.set(context="talk", style="white", font_scale=1.2)
Qs_age_df.plot(kind='bar')
sns.axlabel(xlabel='Pain State', ylabel = 'Steady State Probability')
plt.xticks(fontsize=14, rotation=45)
plt.title('Steady State Distributions by Age Group',weight='bold')
plt.savefig("Pain MC Figure 4 Age Group.pdf")
In [49]:
Q_THA_stationary_df = pd.DataFrame(Qa_THA_stationary, index=['Asleep','0','1','2','3','4','5','6','7','8','9','10'], columns=['THA'])
Q_TKA_stationary_df = pd.DataFrame(Qa_TKA_stationary, index=['Asleep','0','1','2','3','4','5','6','7','8','9','10'], columns=['TKA'])
Q_SpineFusion_stationary_df = pd.DataFrame(Qa_SpineFusion_stationary, index=['Asleep','0','1','2','3','4','5','6','7','8','9','10'], columns=['Spine Fusion'])
Q_Hysterectomy_stationary_df = pd.DataFrame(Qa_Hysterectomy_stationary, index=['Asleep','0','1','2','3','4','5','6','7','8','9','10'], columns=['Hysterectomy'])
Q_HeartValve_stationary_df = pd.DataFrame(Qa_HeartValve_stationary, index=['Asleep','0','1','2','3','4','5','6','7','8','9','10'], columns=['Heart Valve'])

Qs_surgery_df = pd.concat([Q_THA_stationary_df,Q_TKA_stationary_df,Q_SpineFusion_stationary_df,Q_Hysterectomy_stationary_df, Q_HeartValve_stationary_df], axis=1)

sns.set(context="talk", style="white", font_scale=1.2)
Qs_surgery_df.plot(kind='bar')
sns.axlabel(xlabel='Pain State', ylabel = 'Steady State Probability')
plt.xticks(fontsize=14, rotation=45)
plt.title('Steady State Distributions by Type of Surgery',weight='bold')
plt.savefig("Pain MC Figure 4 Type of Surgery.pdf")

First Passage Times to State n

Here, we calculate the number of transition steps required for a subject to move from an initial state to a target state denoted as $n$.

Let $m_u(i, j)$ be the expected number of transitions it takes to reach state $j$ for the first time starting from initial state $i$ .

Assuming $F_i(j,\infty) =1$ (i.e., $j$ is reachable from $i$): $$ m_u(i,j) = 1 + \sum_{k \neq j}p_{ik}*m_u(k,j) $$.

Define mu(j) as the n-1 vector with entries $m_u(1,j), m_u(2,j).., m_u(i,j)$ where $i \neq j$.

Define $Q_j$ as the $(n-1) \times (n-1)$ matrix P with $j_{th}$ row and column deleted.

Then, $$m_u(j) = (I - Qj )^{-1}e$$

where $e$ is the vector of ones.

In [8]:
def Mu(a,j):
    Q = np.asmatrix(a, float) #uses arrays instead of matrix for input
    n = len(Q)-1
    muj = scipy.zeros(n)
    e = scipy.ones(n)  
    Qj = np.identity(n,float)
    I = np.identity(n,float)
    for i in range(j):
        for k in range(j):
            Qj[i,k] = Q[i,k]
        for k in range(j+1,n+1):
            Qj[i,k-1]=Q[i,k]
    for i in range(j+1,n+1):
        for k in range(j):
            Qj[i-1,k] = Q[i,k]
        for k in range(j+1,n+1):
            Qj[i-1,k-1]=Q[i,k]        
    IQj = I-Qj
    IQjinv = la.inv(IQj)
    muj = np.dot(IQjinv,e)
    return muj

Create Matrix of First Passage Times for Range of Initial and Destination States

In [53]:
#First Passage Times from State i to State j, for all subjects and initial states; this ignores starting in that state
#Validated against Mu function above
def FPTM (a):
    Qn=np.asmatrix(a, float)
    Qt = Qn.copy()
    FPM=np.zeros((12, 12))
    for x in range (0,12): #One row for each destination state for first passage time; 12 states, 12 rows
        M = Mu(Qt,x) #Array of first passage times for that destination, each column an initial state; this only gives 11 columns, however, because self is not included
        for y in range(0,11): #Fill matrix y: row from x, column from M[y], but skip if initial and destination states the same
            if y < x: #If column value less than x, no skip and instead insert indexed value of M into indexed FPM cell
                FPM[x,y]=M[y]
            if y > x-1:
                FPM[x,y+1]=M[y]
            if y==x:
                FPM[x,y]=0
    FPM_df = pd.DataFrame(FPM,index=['Asleep','0','1','2','3','4','5','6','7','8','9','10'], columns=['Asleep','0','1','2','3','4','5','6','7','8','9','10'])
    return FPM_df      

Create Heatmap of First Passage Times for Range of Initial and Destination States

In [55]:
FPTM_df = FPTM(Qa)
plt.title('First Passage Times \n from State i (Columns) to State j (rows)', weight='bold')
sns.heatmap(FPTM_df, annot=True, linewidths=0, square=True, cmap='rainbow')
sns.axlabel(xlabel='Initial Pain State', ylabel = 'Destination Pain State')
plt.yticks(rotation=0)
# ax.tick_params(labelbottom='off',labeltop='on')
plt.savefig("Pain MC Figure 5 First Passage Times.pdf")

Stratify First Passage Times for a Discrete Destination State

In [56]:
print 'First Passage Times from State n to State 0:' 
#Note zero indexing and use of Asleep before zero!!! NRS State 1 = column index 2
print list(Q)
print
print 'All subjects:'
Qa_fpt_to1 = Mu(Qa,1); print Qa_fpt_to1
print 'By Sex:'
Qa_male_fpt_to1 = Mu(Qa_male,1); print Qa_male_fpt_to1, 'Male'
Qa_female_fpt_to1 = Mu(Qa_female,1); print Qa_female_fpt_to1, 'Female'
print 'By Age Group:'
Qa_21_39_fpt_to1 = Mu(Qa_21_39,1); print Qa_21_39_fpt_to1, '21-39 years'
Qa_40_64_fpt_to1 = Mu(Qa_40_64,1); print Qa_40_64_fpt_to1, '40-64 years'
Qa_65_84_fpt_to1 = Mu(Qa_65_84,1); print Qa_65_84_fpt_to1, '65-84 years'
Qa_85_orGreater_fpt_to1 = Mu(Qa_85_orGreater,1); print Qa_85_orGreater_fpt_to1, '>= 85 years'
print 'By Type of Surgery:'
Qa_THA_fpt_to1 = Mu(Qa_THA,1); print Qa_THA_fpt_to1, 'THA'
Qa_TKA_fpt_to1 = Mu(Qa_TKA,1); print Qa_TKA_fpt_to1, 'TKA'
Qa_SpineFusion_fpt_to1 = Mu(Qa_SpineFusion,1); print Qa_SpineFusion_fpt_to1, 'Spinal Fusion'
Qa_Hysterectomy_fpt_to1 = Mu(Qa_Hysterectomy,1);print Qa_Hysterectomy_fpt_to1, 'Hysterectomy'
Qa_HeartValve_fpt_to1 = Mu(Qa_HeartValve,1);print Qa_HeartValve_fpt_to1, 'Heart Valve'
#Note also the lack of inclusion of the intial state itself, and so only vector of 11 times shown since time from self to self is not
First Passage Times from State n to State 0:
['Asleep', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '10']

All subjects:
[ 7.01   6.118  6.872  7.191  7.469  7.68   7.971  8.261  8.427  8.765  8.834]
By Sex:
[ 6.841  5.982  6.734  7.138  7.346  7.576  7.874  8.174  8.363  8.693  8.724] Male
[ 7.201  6.267  7.027  7.25   7.604  7.794  8.079  8.36   8.507  8.854  8.957] Female
By Age Group:
[ 10.651   8.854   9.794  10.309  10.656  10.871  11.217  11.455  11.785  12.153  12.295] 21-39 years
[ 8.214  6.976  7.895  8.198  8.524  8.748  9.017  9.336  9.527  9.873  9.997] 40-64 years
[ 5.014  4.665  5.145  5.34   5.506  5.66   5.904  6.131  6.189  6.453  6.429] 65-84 years
[ 3.822  3.81   3.934  4.249  4.25   4.415  4.73   4.944  4.726  4.947  5.246] >= 85 years
By Type of Surgery:
[  8.991   7.909   8.87    9.653   9.992  10.248  10.731  10.934  11.063  11.568  11.499] THA
[ 11.211   9.901  11.003  12.042  12.669  12.583  13.124  13.23   13.44   14.001  13.827] TKA
[ 10.132   9.349  10.203  10.043  10.573  10.749  11.161  11.241  11.539  11.988  12.053] Spinal Fusion
[ 6.27   5.63   5.827  5.955  5.981  6.561  6.712  6.986  7.515  7.742  8.231] Hysterectomy
[ 4.774  4.435  4.815  5.05   4.908  5.062  5.299  5.512  5.815  5.968  6.055] Heart Valve

Probability Distribution for First Passage Times

If $T_{ij}$ is the first time the process enters state $j$ starting in $i$, then the probability distribution of first passage times $F_i$ for all starting states $i$: $$F_i = P(T_{ij} = t) , i \neq j$$

Note that these are not cumulative.

In [57]:
def Fi(a,j,t) :
    Q = np.asmatrix(a, float) #uses arrays instead of matrix for input
    Qt =Q.copy() 
    n = len(Q) #defines number of states
    f = scipy.zeros(n) #initializes 
    ft = scipy.zeros(n)
    for i in range(n):
        f[i] = Qt[i,j] # for target state j, f[i] gives probabilities of arriving at that state from each of the 12 possible initial states      
        Qt[i,j] = 0 #sets the cell for each row i in target state j's column equal to zero; e.g. this is Qt where the destination column j's values all zero and renders this nonrecurrent
    Qtp = Qt**(t-1) #exponentiates the modified transition matrix Qt, which is now modified by zero'd jth column values, 
    ft = np.dot(Qtp,f)
    return ft
In [59]:
print 'Probability that the first passage time to state 0, from any state, is 5 (noncumulative):' #again zero indexing and asleep
print list(Q)
print
print 'All subjects:'
Qa_fptpd_to0_is5 = Fi(Qa,1,5); print Qa_fptpd_to0_is5
print 'By Sex:'
Qa_male_fptpd_to0_is5 = Fi(Qa_male,1,5); print Qa_male_fptpd_to0_is5, 'Male'
Qa_female_fptpd_to0_is5 = Fi(Qa_female,1,5); print Qa_female_fptpd_to0_is5, 'Female'
print 'By Age Group:'
Qa_21_39_fptpd_to0_is5 = Fi(Qa_21_39,1,5); print Qa_21_39_fptpd_to0_is5, '21-39 years'
Qa_40_64_fptpd_to0_is5 = Fi(Qa_40_64,1,5); print Qa_40_64_fptpd_to0_is5, '40-64 years'
Qa_65_84_fptpd_to0_is5 = Fi(Qa_65_84,1,5); print Qa_65_84_fptpd_to0_is5, '65-84 years'
Qa_85_orGreater_fptpd_to0_is5 = Fi(Qa_85_orGreater,1,5); print Qa_85_orGreater_fptpd_to0_is5, '>= 85 years'
print 'By Type of Surgery:'
Qa_THA_fptpd_to0_is5 = Fi(Qa_THA,1,5); print Qa_THA_fptpd_to0_is5, 'THA'
Qa_TKA_fptpd_to0_is5 = Fi(Qa_TKA,1,5); print Qa_TKA_fptpd_to0_is5, 'TKA'
Qa_SpineFusion_fptpd_to0_is5 = Fi(Qa_SpineFusion,1,5); print Qa_SpineFusion_fptpd_to0_is5, 'Spinal Fusion'
Qa_Hysterectomy_fptpd_to0_is5 = Fi(Qa_Hysterectomy,1,5);print Qa_Hysterectomy_fptpd_to0_is5, 'Hysterectomy'
Qa_HeartValve_fptpd_to0_is5 = Fi(Qa_HeartValve,1,5);print Qa_HeartValve_fptpd_to0_is5, 'Heart Valve'
Probability that the first passage time to state 0, from any state, is 5 (noncumulative):
['Asleep', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '10']

All subjects:
[[ 0.067  0.026  0.059  0.067  0.07   0.073  0.075  0.078  0.081  0.082  0.084  0.084]]
By Sex:
[[ 0.067  0.025  0.059  0.067  0.072  0.074  0.076  0.079  0.082  0.083  0.085  0.085]] Male
[[ 0.067  0.028  0.058  0.066  0.069  0.072  0.074  0.077  0.079  0.08   0.083  0.083]] Female
By Age Group:
[[ 0.059  0.028  0.05   0.055  0.058  0.06   0.061  0.063  0.064  0.065  0.066  0.066]] 21-39 years
[[ 0.064  0.027  0.056  0.064  0.066  0.068  0.07   0.072  0.074  0.075  0.077  0.077]] 40-64 years
[[ 0.072  0.026  0.066  0.075  0.078  0.081  0.084  0.088  0.092  0.093  0.097  0.096]] 65-84 years
[[ 0.069  0.024  0.069  0.072  0.08   0.08   0.084  0.092  0.098  0.092  0.098  0.106]] >= 85 years
By Type of Surgery:
[[ 0.056  0.023  0.055  0.06   0.064  0.066  0.066  0.069  0.069  0.069  0.071  0.07 ]] THA
[[ 0.051  0.021  0.049  0.053  0.057  0.059  0.058  0.06   0.06   0.06   0.061  0.06 ]] TKA
[[ 0.057  0.026  0.054  0.06   0.058  0.061  0.062  0.064  0.064  0.065  0.067  0.066]] Spinal Fusion
[[ 0.073  0.048  0.066  0.069  0.071  0.071  0.079  0.081  0.083  0.09   0.09   0.092]] Hysterectomy
[[ 0.076  0.024  0.07   0.078  0.083  0.08   0.083  0.088  0.092  0.098  0.1    0.102]] Heart Valve

Cumulative Sum of Probability Distributions for First Passage Times

We can modify the above equation for $F_i$ to consider the cumulative sum of probabilities for first passage times over sequential transitions.

In [60]:
def Fi_cspd(a,j,t):
    Qn = np.asmatrix(a, float) #uses arrays instead of matrix for input
    Qt = Qn.copy()
    n=len(Qt)
    f = scipy.zeros(n)
    ft = scipy.zeros(n)
    z=np.zeros((t,12))

    for i in range(n):
        f[i] = Qt[i,j]      
        Qt[i,j] = 0
    for l in range(t):
        Qtp = Qt**(l) 
        ft = np.dot(Qtp,f)
        for x in range(n):
            z[l,x] = ft[0,x]
#     print 'Matrix of Probabilities that the First Passage Time to State j, from any State i (column), is t (row):' 
#     print z
    z_cumprob = np.sum(z,axis=0)
#     print
#     print 'Cumulative Sum of Probabilities That the First Passage Time to State j, from any State i, Occurs Within the First t Steps:'
#     print z_cumprob
    return z_cumprob

Cumulative Sum of Probability Distributions for First Passage Times

In [61]:
Fi_cspd(Qa,1,5)#again zero indexing and asleep
Out[61]:
array([ 0.556,  0.826,  0.628,  0.568,  0.543,  0.52 ,  0.503,  0.48 ,  0.456,  0.442,  0.414,  0.408])

Stratified Cumulative Sum of Probability Distributions for First Passage Times

In [62]:
print 'Probability that the cumulative Fi_cspdrst passage time to state 0, from any state, is n(5):'
print 'Alternatively, probability that the cumulative Fi_cspdrst passage time to state 0 occurs within n(5) steps:'
 #again zero indexing and asleep
print list(Q)
print
print 'All subjects:'
Qa_cumfptpd_to0_is5 = Fi_cspd(Qa,1,5); print Qa_cumfptpd_to0_is5
print 'By Sex:'
Qa_male_cumfptpd_to0_is5 = Fi_cspd(Qa_male,1,5); print Qa_male_cumfptpd_to0_is5, 'Male'
Qa_female_cumfptpd_to0_is5 = Fi_cspd(Qa_female,1,5); print Qa_female_cumfptpd_to0_is5, 'Female'
print 'By Age Group:'
Qa_21_39_cumfptpd_to0_is5 = Fi_cspd(Qa_21_39,1,5); print Qa_21_39_cumfptpd_to0_is5, '21-39 years'
Qa_40_64_cumfptpd_to0_is5 = Fi_cspd(Qa_40_64,1,5); print Qa_40_64_cumfptpd_to0_is5, '40-64 years'
Qa_65_84_cumfptpd_to0_is5 = Fi_cspd(Qa_65_84,1,5); print Qa_65_84_cumfptpd_to0_is5, '65-84 years'
Qa_85_orGreater_cumfptpd_to0_is5 = Fi_cspd(Qa_85_orGreater,1,5); print Qa_85_orGreater_cumfptpd_to0_is5, '>= 85 years'
print 'By Type of Surgery:'
Qa_THA_cumfptpd_to0_is5 = Fi_cspd(Qa_THA,1,5); print Qa_THA_cumfptpd_to0_is5, 'THA'
Qa_TKA_cumfptpd_to0_is5 = Fi_cspd(Qa_TKA,1,5); print Qa_TKA_cumfptpd_to0_is5, 'TKA'
Qa_SpineFusion_cumfptpd_to0_is5 = Fi_cspd(Qa_SpineFusion,1,5); print Qa_SpineFusion_cumfptpd_to0_is5, 'Spinal Fusion'
Qa_Hysterectomy_cumfptpd_to0_is5 = Fi_cspd(Qa_Hysterectomy,1,5);print Qa_Hysterectomy_cumfptpd_to0_is5, 'Hysterectomy'
Qa_HeartValve_cumfptpd_to0_is5 = Fi_cspd(Qa_HeartValve,1,5);print Qa_HeartValve_cumfptpd_to0_is5, 'Heart Valve'
Probability that the cumulative Fi_cspdrst passage time to state 0, from any state, is n(5):
Alternatively, probability that the cumulative Fi_cspdrst passage time to state 0 occurs within n(5) steps:
['Asleep', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '10']

All subjects:
[ 0.556  0.826  0.628  0.568  0.543  0.52   0.503  0.48   0.456  0.442  0.414  0.408]
By Sex:
[ 0.566  0.839  0.637  0.576  0.543  0.526  0.507  0.483  0.458  0.442  0.414  0.411] Male
[ 0.544  0.811  0.62   0.56   0.542  0.514  0.499  0.476  0.454  0.442  0.413  0.404] Female
By Age Group:
[ 0.405  0.721  0.521  0.461  0.428  0.406  0.392  0.369  0.354  0.332  0.308  0.298] 21-39 years
[ 0.497  0.789  0.59   0.522  0.499  0.475  0.458  0.438  0.414  0.399  0.373  0.363] 40-64 years
[ 0.678  0.882  0.711  0.667  0.649  0.634  0.62   0.598  0.577  0.571  0.545  0.546] 65-84 years
[ 0.778  0.923  0.783  0.769  0.74   0.739  0.723  0.694  0.673  0.692  0.674  0.645] >= 85 years
By Type of Surgery:
[ 0.48   0.8    0.559  0.492  0.438  0.414  0.397  0.364  0.35   0.341  0.306  0.31 ] THA
[ 0.414  0.763  0.495  0.429  0.367  0.33   0.335  0.303  0.296  0.284  0.25   0.261] TKA
[ 0.433  0.746  0.485  0.431  0.44   0.406  0.395  0.368  0.362  0.343  0.314  0.309] Spinal Fusion
[ 0.588  0.737  0.644  0.627  0.617  0.615  0.567  0.553  0.528  0.482  0.459  0.413] Hysterectomy
[ 0.693  0.903  0.726  0.69   0.669  0.681  0.668  0.646  0.626  0.596  0.58   0.571] Heart Valve

Calculate Cumulative Probabilities from Each Initial State to Target State over n steps

In [63]:
def Fi_cdf(a,j,u):
    Qn = np.asmatrix(a,float)
    Qt = Qn.copy()
    zz=np.zeros((u,12))
    for n in range(0,u):
        zz[n,:] = Fi_cspd(Qt,j,n)
    return zz
#Each row = one step u, each column = initial state, each cell = probabilitiy of reaching target state j from each initial state i within u steps
In [64]:
#Calculate probability of reaching pain state 0 within 100 steps for each initial state
cdf_matrix = Fi_cdf(Qa,1,100)
print cdf_matrix
[[ 0.     0.     0.    ...,  0.     0.     0.   ]
 [ 0.193  0.688  0.257 ...,  0.072  0.052  0.053]
 [ 0.316  0.733  0.401 ...,  0.17   0.14   0.138]
 ..., 
 [ 1.     1.     1.    ...,  1.     1.     1.   ]
 [ 1.     1.     1.    ...,  1.     1.     1.   ]
 [ 1.     1.     1.    ...,  1.     1.     1.   ]]
In [65]:
cdf_df = pd.DataFrame(cdf_matrix[:30,:], columns=['Asleep','0','1','2','3','4','5','6','7','8','9','10']) #Just 30 rows
print cdf_df
print 0.57-0.26
      Asleep         0         1         2         3         4         5  \
0   0.000000  0.000000  0.000000  0.000000  0.000000  0.000000  0.000000   
1   0.192805  0.688148  0.256817  0.190777  0.166043  0.142274  0.125126   
2   0.316401  0.732661  0.400675  0.322157  0.290292  0.261870  0.240875   
3   0.410663  0.769006  0.496830  0.421499  0.389803  0.362294  0.341618   
4   0.488763  0.799684  0.569508  0.501535  0.472460  0.447454  0.428454   
5   0.555595  0.825994  0.628441  0.568426  0.542559  0.520371  0.503403   
6   0.613440  0.848727  0.677981  0.625439  0.602712  0.583228  0.568274   
7   0.663694  0.868439  0.720365  0.674527  0.654665  0.637637  0.624543   
8   0.707402  0.885561  0.756940  0.717009  0.699693  0.684845  0.673416   
9   0.745429  0.900445  0.788632  0.753870  0.738790  0.725858  0.715898   
10  0.778516  0.913390  0.816149  0.785897  0.772770  0.761513  0.752841   
11  0.807303  0.924649  0.840065  0.813742  0.802319  0.792522  0.784973   
12  0.832349  0.934444  0.860862  0.837959  0.828019  0.819495  0.812926   
13  0.854140  0.942966  0.878951  0.859024  0.850376  0.842959  0.837243   
14  0.873099  0.950379  0.894687  0.877350  0.869825  0.863372  0.858399   
15  0.889594  0.956829  0.908376  0.893292  0.886746  0.881132  0.876805   
16  0.903945  0.962441  0.920286  0.907163  0.901467  0.896583  0.892818   
17  0.916430  0.967323  0.930647  0.919230  0.914275  0.910025  0.906750   
18  0.927292  0.971570  0.939662  0.929729  0.925417  0.921720  0.918871   
19  0.936743  0.975265  0.947505  0.938863  0.935112  0.931895  0.929416   
20  0.944965  0.978480  0.954328  0.946809  0.943546  0.940747  0.938591   
21  0.952119  0.981278  0.960265  0.953723  0.950884  0.948449  0.946573   
22  0.958342  0.983711  0.965430  0.959738  0.957268  0.955150  0.953517   
23  0.963757  0.985828  0.969923  0.964972  0.962823  0.960979  0.959559   
24  0.968468  0.987670  0.973832  0.969525  0.967655  0.966051  0.964816   
25  0.972567  0.989273  0.977234  0.973486  0.971859  0.970464  0.969389   
26  0.976132  0.990667  0.980193  0.976932  0.975517  0.974303  0.973368   
27  0.979235  0.991880  0.982768  0.979931  0.978699  0.977643  0.976830   
28  0.981934  0.992936  0.985007  0.982539  0.981468  0.980549  0.979841   
29  0.984282  0.993854  0.986956  0.984809  0.983877  0.983078  0.982462   

           6         7         8         9        10  
0   0.000000  0.000000  0.000000  0.000000  0.000000  
1   0.101514  0.080514  0.071758  0.051607  0.053190  
2   0.211649  0.183855  0.169977  0.140355  0.138477  
3   0.312844  0.284573  0.269091  0.237122  0.232383  
4   0.402087  0.375749  0.360598  0.329814  0.323692  
5   0.479919  0.456260  0.442284  0.414126  0.407694  
6   0.547616  0.526711  0.514187  0.489063  0.482901  
7   0.606474  0.588147  0.577085  0.554944  0.549308  
8   0.657654  0.641649  0.631950  0.612561  0.607527  
9   0.702167  0.688215  0.679744  0.662818  0.658379  
10  0.740887  0.728737  0.721351  0.706601  0.702710  
11  0.774570  0.763994  0.757562  0.744717  0.741320  
12  0.803873  0.794669  0.789070  0.777890  0.774929  
13  0.829367  0.821358  0.816486  0.806757  0.804178  
14  0.851546  0.844578  0.840338  0.831873  0.829628  
15  0.870842  0.864780  0.861091  0.853726  0.851772  
16  0.887631  0.882356  0.879146  0.872738  0.871038  
17  0.902237  0.897648  0.894855  0.889280  0.887801  
18  0.914944  0.910952  0.908522  0.903671  0.902384  
19  0.926000  0.922526  0.920412  0.916192  0.915073  
20  0.935618  0.932596  0.930757  0.927086  0.926112  
21  0.943987  0.941358  0.939758  0.936563  0.935716  
22  0.951267  0.948980  0.947588  0.944809  0.944071  
23  0.957602  0.955612  0.954401  0.951983  0.951341  
24  0.963113  0.961381  0.960328  0.958224  0.957666  
25  0.967907  0.966401  0.965484  0.963654  0.963169  
26  0.972079  0.970768  0.969971  0.968378  0.967956  
27  0.975708  0.974568  0.973874  0.972489  0.972121  
28  0.978866  0.977874  0.977270  0.976065  0.975745  
29  0.981613  0.980750  0.980224  0.979176  0.978898  
0.31

Visualize the Cumulative Probabilities of Reaching a Target State

In [66]:
# plt.show()
sns.set(context="talk", style="white", font_scale=1.2)
cdf_df.plot()
plt.title('Cumulative Probability of Reaching State 0 in State Transitions', weight='bold')
sns.axlabel(xlabel='Number of Transition Steps', ylabel = 'Cumulative Probability')
plt.savefig("Pain MC Figure 6 CDF.pdf")

Alternative Visualizations of the Cumulative Probabilities of Reaching a Target State

In [67]:
#All subjects
sns.set(context="talk", style="white")

fig = plt.figure(figsize=(10,10))
ax=fig.add_subplot(111)
plt.imshow(cdf_matrix[0:30,:], interpolation='nearest', cmap='jet')
plt.title('Cumulative Probability of Reaching State 0 \n Following n Steps \n All Subjects')
plt.ylabel('Steps until State 0')
plt.clim(0,1)
plt.colorbar().ax.invert_yaxis()
plt.gcf().subplots_adjust(bottom=0.2)
ax.set_xticks([0,1,3,5,7,9,11])
ax.set_xticklabels(['Asleep','0','2','4','6','8','10'],rotation='vertical')
ax.set_xlabel('Initial State')
plt.savefig("Pain MC Figure 6 All.pdf")
plt.show()
In [68]:
n=100 #establish number of steps
print 'Cumulative Per-Step Probabiliities of Reaching Pain State 0 within 100 steps for each initial state by stratifications:'
print 'First 3 steps shown'
 #again zero indexing and asleep
print list(Q)
print
print 'All subjects:'
Qa_cdf_matrix_0_100 = Fi_cdf(Qa,1,n); print Qa_cdf_matrix_0_100[:3,:]
print 'By Sex:'
Qa_male_cdf_matrix_0_100 = Fi_cdf(Qa_male,1,n); print Qa_male_cdf_matrix_0_100[:3,:], 'Male'
Qa_female_cdf_matrix_0_100 = Fi_cdf(Qa_female,1,n); print Qa_female_cdf_matrix_0_100[:3,:], 'Female'
print 'By Age Group:'
Qa_21_39_cdf_matrix_0_100 = Fi_cdf(Qa_21_39,1,n); print Qa_21_39_cdf_matrix_0_100[:3,:], '21-39 years'
Qa_40_64_cdf_matrix_0_100 = Fi_cdf(Qa_40_64,1,n); print Qa_40_64_cdf_matrix_0_100[:3,:], '40-64 years'
Qa_65_84_cdf_matrix_0_100 = Fi_cdf(Qa_65_84,1,n); print Qa_65_84_cdf_matrix_0_100[:3,:], '65-84 years'
Qa_85_orGreater_cdf_matrix_0_100 = Fi_cdf(Qa_85_orGreater,1,n); print Qa_85_orGreater_cdf_matrix_0_100[:3,:], '>= 85 years'
print 'By Type of Surgery:'
Qa_THA_cdf_matrix_0_100 = Fi_cdf(Qa_THA,1,n); print Qa_THA_cdf_matrix_0_100[:3,:], 'THA'
Qa_TKA_cdf_matrix_0_100 = Fi_cdf(Qa_TKA,1,n); print Qa_TKA_cdf_matrix_0_100[:3,:], 'TKA'
Qa_SpineFusion_cdf_matrix_0_100 = Fi_cdf(Qa_SpineFusion,1,n); print Qa_SpineFusion_cdf_matrix_0_100[:3,:], 'Spinal Fusion'
Qa_Hysterectomy_cdf_matrix_0_100 = Fi_cdf(Qa_Hysterectomy,1,n);print Qa_Hysterectomy_cdf_matrix_0_100[:3,:], 'Hysterectomy'
Qa_HeartValve_cdf_matrix_0_100 = Fi_cdf(Qa_HeartValve,1,n);print Qa_HeartValve_cdf_matrix_0_100[:3,:], 'Heart Valve'
Cumulative Per-Step Probabiliities of Reaching Pain State 0 within 100 steps for each initial state by stratifications:
First 3 steps shown
['Asleep', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '10']

All subjects:
[[ 0.     0.     0.     0.     0.     0.     0.     0.     0.     0.     0.     0.   ]
 [ 0.193  0.688  0.257  0.191  0.166  0.142  0.125  0.102  0.081  0.072  0.052  0.053]
 [ 0.316  0.733  0.401  0.322  0.29   0.262  0.241  0.212  0.184  0.17   0.14   0.138]]
By Sex:
[[ 0.     0.     0.     0.     0.     0.     0.     0.     0.     0.     0.     0.   ]
 [ 0.197  0.707  0.257  0.193  0.16   0.144  0.125  0.1    0.079  0.068  0.051  0.055]
 [ 0.324  0.75   0.405  0.327  0.285  0.265  0.241  0.211  0.182  0.166  0.139  0.141]] Male
[[ 0.     0.     0.     0.     0.     0.     0.     0.     0.     0.     0.     0.   ]
 [ 0.188  0.665  0.256  0.188  0.172  0.14   0.125  0.103  0.082  0.075  0.052  0.051]
 [ 0.308  0.712  0.396  0.317  0.295  0.259  0.24   0.212  0.185  0.173  0.142  0.137]] Female
By Age Group:
[[ 0.     0.     0.     0.     0.     0.     0.     0.     0.     0.     0.     0.   ]
 [ 0.122  0.586  0.223  0.165  0.13   0.108  0.098  0.077  0.067  0.052  0.036  0.037]
 [ 0.208  0.627  0.337  0.266  0.226  0.2    0.185  0.16   0.145  0.123  0.1    0.096]] 21-39 years
[[ 0.     0.     0.     0.     0.     0.     0.     0.     0.     0.     0.     0.   ]
 [ 0.164  0.651  0.245  0.172  0.153  0.128  0.113  0.095  0.074  0.065  0.047  0.045]
 [ 0.273  0.695  0.378  0.292  0.266  0.236  0.217  0.193  0.166  0.152  0.125  0.119]] 40-64 years
[[ 0.     0.     0.     0.     0.     0.     0.     0.     0.     0.     0.     0.   ]
 [ 0.244  0.733  0.278  0.221  0.201  0.182  0.162  0.133  0.108  0.104  0.083  0.091]
 [ 0.401  0.784  0.445  0.379  0.353  0.331  0.309  0.276  0.246  0.239  0.207  0.214]] 65-84 years
[[ 0.     0.     0.     0.     0.     0.     0.     0.     0.     0.     0.     0.   ]
 [ 0.307  0.771  0.294  0.282  0.232  0.235  0.21   0.161  0.131  0.172  0.124  0.082]
 [ 0.492  0.827  0.486  0.468  0.415  0.416  0.388  0.335  0.299  0.339  0.298  0.247]] >= 85 years
By Type of Surgery:
[[ 0.     0.     0.     0.     0.     0.     0.     0.     0.     0.     0.     0.   ]
 [ 0.201  0.681  0.209  0.159  0.115  0.099  0.086  0.058  0.047  0.047  0.021  0.035]
 [ 0.291  0.72   0.343  0.273  0.214  0.192  0.176  0.139  0.126  0.121  0.087  0.097]] THA
[[ 0.     0.     0.     0.     0.     0.     0.     0.     0.     0.     0.     0.   ]
 [ 0.177  0.654  0.194  0.143  0.092  0.063  0.07   0.044  0.042  0.037  0.014  0.029]
 [ 0.248  0.69   0.309  0.24   0.175  0.137  0.145  0.111  0.107  0.097  0.065  0.081]] TKA
[[ 0.     0.     0.     0.     0.     0.     0.     0.     0.     0.     0.     0.   ]
 [ 0.155  0.622  0.184  0.119  0.139  0.105  0.097  0.072  0.073  0.059  0.035  0.043]
 [ 0.243  0.659  0.292  0.22   0.237  0.197  0.186  0.156  0.153  0.133  0.102  0.106]] Spinal Fusion
[[ 0.     0.     0.     0.     0.     0.     0.     0.     0.     0.     0.     0.   ]
 [ 0.195  0.473  0.248  0.226  0.208  0.206  0.139  0.134  0.118  0.067  0.069  0.053]
 [ 0.325  0.561  0.392  0.368  0.352  0.349  0.279  0.263  0.237  0.178  0.166  0.132]] Hysterectomy
[[ 0.     0.     0.     0.     0.     0.     0.     0.     0.     0.     0.     0.   ]
 [ 0.234  0.763  0.274  0.226  0.193  0.214  0.191  0.158  0.132  0.103  0.092  0.085]
 [ 0.397  0.811  0.446  0.389  0.353  0.376  0.352  0.316  0.285  0.244  0.225  0.215]] Heart Valve
In [73]:
fig = plt.figure(figsize=(20,10))

ax=fig.add_subplot(121)
plt.imshow(Qa_male_cdf_matrix_0_100[0:30,:], interpolation='nearest',cmap='jet')
plt.title('Cumulative Probability of Reaching State 0 \n Following n Steps \n Male Subjects')
plt.xlabel('Initial State')
plt.ylabel('Steps until State 0')

ax.set_xticks([0,1,3,5,7,9,11])
ax.set_xticklabels(['Asleep','0','2','4','6','8','10'],rotation='vertical')
plt.gcf().subplots_adjust(bottom=0.2)
plt.savefig("Pain MC Figure 6 Male.pdf")

ax=fig.add_subplot(122)
plt.imshow(Qa_female_cdf_matrix_0_100[0:30,:], interpolation='nearest',cmap='jet')
plt.title('Cumulative Probability of Reaching State 0 \n Following n Steps \n Female Subjects')
plt.xlabel('Initial State')

plt.clim(0,1)
plt.colorbar().ax.invert_yaxis()
ax.set_xticks([0,1,3,5,7,9,11])
ax.set_xticklabels(['Asleep','0','2','4','6','8','10'],rotation='vertical')
plt.gcf().subplots_adjust(bottom=0.2,right=0.75)
plt.savefig("Pain MC Figure 6 Female.pdf")


plt.show()
In [103]:
#Age Group
fig = plt.figure(figsize=(20,10))

ax=fig.add_subplot(141)
plt.imshow(Qa_21_39_cdf_matrix_0_100[0:30,:], interpolation='nearest', cmap='jet')
plt.title('Cumulative Probability of Reaching State 0 \n Following n Steps \n 21-39 Years')
plt.xlabel('Initial State')
plt.ylabel('Steps until State 0')

ax.set_xticks([0,1,3,5,7,9,11])
ax.set_xticklabels(['Asleep','0','2','4','6','8','10'],rotation='vertical')
plt.gcf().subplots_adjust(bottom=0.2,right=0.75)

ax=fig.add_subplot(142)
plt.imshow(Qa_40_64_cdf_matrix_0_100[0:30,:], interpolation='nearest', cmap='jet')
plt.title('Cumulative Probability of Reaching State 0 \n Following n Steps \n 40-64 Years ')
plt.xlabel('Initial State')
plt.ylabel('Steps until State 0')

ax.set_xticks([0,1,3,5,7,9,11])
ax.set_xticklabels(['Asleep','0','2','4','6','8','10'],rotation='vertical')
plt.gcf().subplots_adjust(bottom=0.2,right=0.75)

ax=fig.add_subplot(143)
plt.imshow(Qa_65_84_cdf_matrix_0_100[0:30,:], interpolation='nearest', cmap='jet')
plt.title('Cumulative Probability of Reaching State 0 \n Following n Steps \n 65-84 Years')
plt.xlabel('Initial State')
plt.ylabel('Steps until State 0')

ax.set_xticks([0,1,3,5,7,9,11])
ax.set_xticklabels(['Asleep','0','2','4','6','8','10'],rotation='vertical')
plt.gcf().subplots_adjust(bottom=0.2,right=0.75)

ax=fig.add_subplot(144)
plt.imshow(Qa_85_orGreater_cdf_matrix_0_100[0:30,:], interpolation='nearest', cmap='jet')
plt.title('Cumulative Probability of Reaching State 0 \n Following n Steps \n 85 or Greater Years')
plt.xlabel('Initial State')
plt.ylabel('Steps until State 0')

ax.set_xticks([0,1,3,5,7,9,11])
ax.set_xticklabels(['Asleep','0','2','4','6','8','10'],rotation='vertical')
plt.gcf().subplots_adjust(bottom=0.2,right=1)

plt.clim(0,1)
plt.colorbar().ax.invert_yaxis()
plt.savefig("Pain MC Figure 6 Age Group.pdf")

plt.show() 
In [112]:
# Type of Surgery

fig = plt.figure(figsize=(30,10))

ax=fig.add_subplot(151)
plt.imshow(Qa_THA_cdf_matrix_0_100[0:30,:], interpolation='nearest', cmap='jet')
plt.title('Cumulative Probability of Reaching State 0 \n Following n Steps \n THA')
plt.xlabel('Initial State')
plt.ylabel('Steps until State 0')

ax.set_xticks([0,1,3,5,7,9,11])
ax.set_xticklabels(['Asleep','0','2','4','6','8','10'],rotation='vertical')
plt.gcf().subplots_adjust(bottom=0.2,right=0.75)

ax=fig.add_subplot(152)
plt.imshow(Qa_TKA_cdf_matrix_0_100[0:30,:], interpolation='nearest', cmap='jet')
plt.title('Cumulative Probability of Reaching State 0 \n Following n Steps \n TKA')
plt.xlabel('Initial State')
plt.ylabel('Steps until State 0')

ax.set_xticks([0,1,3,5,7,9,11])
ax.set_xticklabels(['Asleep','0','2','4','6','8','10'],rotation='vertical')
plt.gcf().subplots_adjust(bottom=0.2,right=0.75)

ax=fig.add_subplot(153)
plt.imshow(Qa_SpineFusion_cdf_matrix_0_100[0:30,:], interpolation='nearest', cmap='jet')
plt.title('Cumulative Probability of Reaching State 0 \n Following n Steps \n Spine Fusion')
plt.xlabel('Initial State')
plt.ylabel('Steps until State 0')

ax.set_xticks([0,1,3,5,7,9,11])
ax.set_xticklabels(['Asleep','0','2','4','6','8','10'],rotation='vertical')
plt.gcf().subplots_adjust(bottom=0.2,right=0.75)

ax=fig.add_subplot(154)
plt.imshow(Qa_Hysterectomy_cdf_matrix_0_100[0:30,:], interpolation='nearest', cmap='jet')
plt.title('Cumulative Probability of Reaching State 0 \n Following n Steps \n Hysterectomy')
plt.xlabel('Initial State')
plt.ylabel('Steps until State 0')

ax.set_xticks([0,1,3,5,7,9,11])
ax.set_xticklabels(['Asleep','0','2','4','6','8','10'],rotation='vertical')
plt.gcf().subplots_adjust(bottom=0.2,right=0.75)

ax=fig.add_subplot(155)
plt.imshow(Qa_HeartValve_cdf_matrix_0_100[0:30,:], interpolation='nearest', cmap='jet')
plt.title('Cumulative Probability of Reaching State 0 \n Following n Steps \n Heart Valve')
plt.xlabel('Initial State')
plt.ylabel('Steps until State 0')

ax.set_xticks([0,1,3,5,7,9,11])
ax.set_xticklabels(['Asleep','0','2','4','6','8','10'],rotation='vertical')
plt.gcf().subplots_adjust(bottom=0.2, right=0.75)

plt.clim(0,1)
plt.colorbar().ax.invert_yaxis()
plt.savefig("Pain MC Figure 5 Type of Surgery.pdf")



plt.show() 

Visualize TM in Network Visualization

In [74]:
D = nx.from_numpy_matrix(Qa)
G=nx.DiGraph(D)
G.edges(data=True)
values = range(144)

pos=nx.circular_layout(G) # positions for all nodes

# nodes
nx.draw_networkx_nodes(G,pos,node_color='b',node_size=3000,alpha=0.9)


# edges
jet = cm = plt.get_cmap('Blues') 
cNorm  = colors.Normalize(vmin=0, vmax=values[-1])
scalarMap = cmx.ScalarMappable(norm=cNorm, cmap=jet)
colorList = []

for i in range(144):
    colorVal = scalarMap.to_rgba(values[i])
    colorList.append(colorVal)


nx.draw_networkx_edges(G,pos,width=1.0,alpha=0.75, edge_color=colorList, arrows=True)

# some math labels
labels={}
labels[0]='Asleep'
labels[1]='0'
labels[2]='1'
labels[3]='2'
labels[4]='3'
labels[5]='4'
labels[6]='5'
labels[7]='6'
labels[8]='7'
labels[9]='8'
labels[10]='9'
labels[11]='10'

nx.draw_networkx_labels(G,pos,labels,font_size=16, font_color='white', font_weight='bold')

plt.axis('off')
# plt.savefig("labels_and_colors.png") # save as png
plt.show() # display
In [ ]: