-
Notifications
You must be signed in to change notification settings - Fork 4
/
RandomiseDesignFileSetup.py
executable file
·696 lines (574 loc) · 25.6 KB
/
RandomiseDesignFileSetup.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
#!/usr/bin/env python
"""
Name: DesignFileSetup.py
Created by: Kirstie Whitaker
kw401@cam.ac.uk
This script takes a behavioral data file for MRIMPACT data
and a list of mri subjects who have usable data and then
creates a plethora of possible design files for randomise
analyes in FSL.
You could also use these files for feat analyses by copying
the numbers into the "paste" window in the Feat GUI.
Version 0.1: 3rd April 2013
Currently this script works but it needs to be
tested rigorously. Also it currently uses random data
for cortisol measurements.
TO DO: Include grand mean measurement for Feat analyses
"""
#------------------------------------------------
### IMPORTS ###
#------------------------------------------------
import os
import sys
import numpy as np
from shutil import copy, rmtree
import itertools as it
import numpy.lib.recfunctions as nprf
from glob import glob
import filecmp
# Import your personal scripts
sys.path.insert(0, '/home/kw401/CAMBRIDGE_SCRIPTS/GENERAL_SCRIPTS/')
sys.path.insert(0, 'C:\\Users\\Kirstie\\Dropbox\\GitHub\\GENERAL_CODE\\')
import MyCoolFunctions as mcf
#------------------------------------------------
#------------------------------------------------
### FUNCTIONS ###
#------------------------------------------------
def usage():
"""
Prints the usage to the command line
Input: None
Output: Text to terminal
"""
print 'Randomise_setup.py <behav_file> <cortisol_file> <randomise_setup.py file> <usable_mri_sublist_file> <output_directory> '
print ' <behav_file>: comma separated behavioral data with header'
print ' <cortisol_file>: tab delimited file with cortisol data'
print ' probably output from Cortisol_PreProcessing.py'
print ' <randomise_setup.py file>: randomise file that contains your personal options'
print ' <useable_mri_sublist_file>: list of subids to be included in analyses'
print ' <output_directory>: Name (and path) of output directory'
print '\teg: Randomise_DTIROIS_setup.py BehavData_130321.csv dti_sublist TBSS_120314'
#------------------------------------------------
def setup_data(behav_filename):
data = np.genfromtxt(behav_filename, dtype=None, names=True, delimiter=',')
if not 'SubID' in data.dtype.names:
### SUBID
# Create a SubID column that is just the subid without the letter M
subids = data['ParticipantID']
subids = np.char.strip(subids, 'M')
subids = subids.astype(int)
data = nprf.append_fields(base = data, names = 'SubID',
data = subids, usemask=False)
if not 'TreatmentArm' in data.dtype.names:
### TREATMENTARM
# Convert the TreatmentGroup into TreatmentArm integer values
treatment_dict = dict()
treatment_dict[0] = 'Control'
treatment_dict[1] = 'CBT'
treatment_dict[2] = 'STPP'
treatment_dict[3] = 'SCC'
treatment_arm_array = np.ones(data['TreatmentGroup'].shape) * 999
for i in range(4):
treatment_arm_array[data['TreatmentGroup']==treatment_dict[i]] = i
treatment_arm_array = treatment_arm_array.astype(int)
data = nprf.append_fields(base = data, names='TreatmentArm',
data = treatment_arm_array, usemask=False)
if not 'Depressed' in data.dtype.names:
### DEPRESSED
# Add in a column called Depressed - based on the TreatmentArm
data = nprf.append_fields(base = data, names='Depressed',
data = data['TreatmentArm'], usemask=False)
# Replace the values in Depressed with 1s and 0s
data['Depressed'][data['Depressed'] > 0] = 1
if not 'Male' in data.dtype.names:
### MALE
# Create Male column of 1s and 0s
data = nprf.append_fields(base = data, names='Male',
data = data['Sex'], usemask=False)
data['Male'][data['Sex'] == 'M'] = 1
data['Male'][data['Sex'] == 'F'] = 0
return data
def merge_cort(data, cortisol_filename):
cort_data = np.genfromtxt(cortisol_filename, dtype=None, names=True, delimiter='\t')
names = list(cort_data.dtype.names)
# Find all the columns in cort_data that have 'av' in their title
# and not '_mask'
drop_names = names[8:]
cort_data = nprf.drop_fields(cort_data, drop_names, usemask=False, asrecarray=True)
data = nprf.join_by('SubID', data, cort_data, jointype='leftouter',
r1postfix='KW', r2postfix='KW2', usemask=False,asrecarray=True)
# Bizzarely, the join_by function pads with the biggest numbers it can think of!
# So we're going to replace everything over 999 with 999
for name in names[1:8]:
data[name][data[name]>999] = 999
# Define a UsableCort field: 1 if ANY of the cortisol values are not 999
cort_array = np.vstack( [ data[name] for name in names[1:8]])
usable_cort_array = np.ones(cort_array.shape[1])
usable_cort_array[np.any(cort_array<>999, axis=0)] = 1
data = nprf.append_fields(base = data, names='UsableCort', data = usable_cort_array, usemask=False)
return data
def make_subs_array(data):
"""
A very simple little function that just adds t1 to the
end of the subIDs incase you need it.
"""
# Create the subs array - adding t1 to the end of each subid
t1 = np.ones_like(data['SubID']).astype(str)
t1[:] = 't1'
subs_array = np.char.add(data['SubID'].astype(str), t1)
return subs_array
#------------------------------------------------
def create_mask_all(data, usable_mri_subs):
"""
This function excludes people who have abnormal brains or braces
and only includes participants who have usable MRI data
"""
# Only include data for people who do not have abnormal brains or braces
ab_brain_mask = data['AbnormalBrain']==0
braces_mask = data['Braces']==0
# Only include subs who have usable mri data
'''
AMAAAAZING tip here - np.in1d
stolen from http://stackoverflow.com/questions/13629061/numpy-mask-based-on-if-a-value-is-in-some-other-list
'''
mri_mask = np.in1d(data['SubID'], usable_mri_subs)
# Generate an overall mask
mask_all = ab_brain_mask * braces_mask * mri_mask
# If you have chosen to require all measures then this
# this function also masks everyone who has 999 for any values
# of any measure
if req_all_measures:
all_vars = set(measures + covars)
all_var_array = np.vstack([ data[var] for var in all_vars ])
# Mask any rows that have a value of 999
all_var_mask = np.all(all_var_array<>999, axis=0)
mask_all = mask_all * all_var_mask
return mask_all
#------------------------------------------------
def excl_999s(data, measure, combo, mask):
all_vars = []
if not measure == '':
all_vars.append(measure)
all_vars.extend(combo)
if not all_vars == []:
all_var_array = np.vstack([ data[var] for var in all_vars ])
# Mask any rows that have a value of 999
all_var_mask = np.all(all_var_array<>999, axis=0)
mask = mask * all_var_mask
return data, mask
def write_files(subs_array, mat_array, con_array, test_name, dir):
"""
Writes out .mat, .con and subs design files
Inputs:
subs_array List of subIDs to be written out
mat_array Data for .mat file
con_array Data for .con file
test_name String that will be part of file name
dir Output_dir
Output:
Files saved in locations specified by filenames:
subs file List of sub ids (just the 4 digit number)
subs_t1_file List of sub ids with t1 appended
mat_file Mat file for FSL analyses
con_file Con file for FSL analyses
"""
# Make sure output dir exists
mcf.KW_mkdirs(dir)
# Name the files
subs_t1_filename = os.path.join(dir, 'subs_t1')
subs_filename = os.path.join(dir, 'subs')
mat_filename = os.path.join(dir, test_name + '.mat')
con_filename = os.path.join(dir, test_name + '.con')
# Write out the subs with t1 appended to the subs_t1_filename
np.savetxt(subs_t1_filename, subs_array[mask], fmt = '%s')
# Write out the subs ids alone to the subs filename
np.savetxt(subs_filename, data['SubID'][mask], fmt = '%s')
# Only save files if there are no empty colums
# (eg: Controls and Meds -- the Meds column will be all 0s)
# ndim is the number of dimensions that the array has
# but we need to index from 1 below that
if mat_array.ndim == 1:
test = mat_array.std() == 0.0
else:
test = 0.0 in mat_array.std(axis=mat_array.ndim-1)
if not test:
# Save the mat data
np.savetxt(mat_filename, mat_array.T, fmt = '%2.6f')
if mat_array.ndim == 1:
waves = 1
points = mat_array.shape[0]
else:
waves = mat_array.shape[0]
points = mat_array.shape[1]
# Now create the matrix header
'''
I stole how to do this from
http://www.gossamer-threads.com/lists/python/dev/736081
'''
# read the current contents of the file
f = open(mat_filename)
text = f.read()
f.close()
# open the file again for writing
f = open(mat_filename, 'w')
f.write('/NumWaves ' + str(waves) + '\n')
f.write('/NumPoints ' + str(points) + '\n')
f.write('/Matrix \n')
# write the original contents
f.write(text)
f.close()
# Repeat for the contrast data
np.savetxt(con_filename, con_array, fmt = '%2.4f')
# Now write the header
# Read in the original data
f = open(con_filename)
text = f.read()
f.close()
# First the header
f = open(con_filename, 'w')
f.write('/NumWaves ' + str(waves) + '\n')
f.write('/NumContrasts ' + str(con_array.shape[0]) + '\n')
f.write('/Matrix \n')
# Now write in the original contents
f.write(text)
f.close()
#------------------------------------------------
def create_arrays(data, mask, measure, combo):
"""
This function creates mat and con arrays and test_name
from data for a measure of interest (measure) and
a combination of covariates of no interest.
Both measure and combo can be empty
INPUTS:
data The rec array that contains the data
mask The mask created by mask_all that
defines which participants you
will include in this array
measure The measure that will be included as
a measure of INTEREST
There can only be ONE measure
(or none)
combo A combination of covariates of NO
interest. This combination can
be as long as needed (including
an empty combination)
"""
# First thing is to put all the data together
# and get rid of any rows that have 999s in them
# (Note that this has to happen before demeaning
# because otherwise they aren't 999s anymore!!)
data, mask = excl_999s(data, measure, combo, mask)
# If measure exists then create a var_array from
# the measure of interest and demean it
if measure:
var_array = data[measure][mask]
var_array = var_array - var_array.mean()
# If combo exists then create a covar_array from
# the covariates of no interest
if combo:
# Stack together the covar data
'''
Another example of lovely list comprehension
http://docs.python.org/2/tutorial/datastructures.html#list-comprehensions
'''
covar_array = np.vstack([ data[covar][mask] for covar in combo ])
# Demean the columns
if covar_array.ndim == 1:
covar_array = covar_array - covar_array.mean()
else:
'''
This is a use of the [None] slicing - it adds a additional axis
so that the 1 dimensional means can be subtracted from the
2 dimensional covar_array
'''
covar_array = covar_array - covar_array.mean(axis=1)[None].T
# The mat array is just the stacking together of both of these arrays
# obviously depending on if you have them all etc
if measure and combo:
mat_array = np.vstack([var_array, covar_array])
test_name = 'Corr_' + measure
covar_name = '_'.join(covar for covar in combo)
test_name = test_name + '_Covar_' + covar_name
elif measure:
test_name = 'Corr_' + measure
mat_array = var_array
elif combo:
covar_name = '_'.join(covar for covar in combo)
test_name = 'Covar_' + covar_name
mat_array = covar_array
else:
test_name = ''
mat_array = np.array([])
# Create a contrast array by creating a bunch of zeros
# and then put 1 and -1 at the beginning of the two lines
template = np.array([[1],[-1]])
if mat_array.ndim == 1 and len(mat_array) > 0:
con_array = template
elif mat_array.ndim > 1:
con_array = np.zeros([2, mat_array.shape[0]])
con_array[:,:1] = template
else:
con_array = np.array([])
return test_name, mat_array, con_array
#------------------------------------------------
def create_correlations(dir, mask, data, subs_array):
"""
This function sets up all the correlations for each subject list
"""
# Create all the correlation design files
# For each individual measure you care about
for measure in measures:
# We're going to loop over all the combinations of
# the various covariates of no interest
# eg: Age, Age_Male, Age_Male_Meds, Male, Male_Meds, Meds
for i in range(0,len(covars)+1):
for combo in it.combinations(covars, i):
# Don't repeat measures
# you'll have the same column in there twice!
if not measure in combo:
# Use create_arrays function to create mat and con arrays
test_name, mat_array, con_array = create_arrays(data, mask, measure, combo)
# Use write_files function to save the files
write_files(subs_array, mat_array, con_array, test_name, dir)
#------------------------------------------------
def create_arrays_ttest(mask, data, perm_list, index, measure, combo):
"""
INPUTS:
mask boolean mask that selects appropriate subjects
data rec array containing the data
mask should fit this data
perm_list list of permutations of covariates
index location within perm_list that you want to test
with the ttest - eg if the list is 2,2,2
and index is 1 then you'll split on the
2nd value in the list and leave the others
as they are
measure covariate of interest
combo combination of addition covariates to include
but NOT test. Covariates of NO interest
"""
# Define the two column arrays - mask here
col1 = data[split_vars[index]][mask]
col2 = (col1 * -1) + 1
ttest_array = np.vstack([col1, col2])
# Use create_arrays function to create mat and con arrays
corr_test_name, mat_array, con_array = create_arrays(data, mask, measure, combo)
if measure or combo:
# Append this mat_array to the ttest_array
ttest_array = np.vstack([ttest_array, mat_array])
# Define the test_name
if measure:
measure_name = '_Corr_' + measure
else:
measure_name = ''
if combo:
covar_name = '_'.join(covar for covar in combo)
covar_name = '_Covar_' + covar_name
else:
covar_name = ''
ttest_name = ( 'TTest_'
+ group_dict[split_vars[index] + '_1']
+ group_dict[split_vars[index] + '_0']
+ measure_name
+ covar_name )
# Create a contrast array by creating a bunch of zeros
# and then put 1 and -1 at the beginning of the two
# lines
con_array = np.zeros([2, ttest_array.shape[0]])
template = np.array([[1,-1],[-1,1]])
con_array[:,:2] = template
# Lastly - we have to create the interactions not just the
# correlations
ttest_array_2col = np.vstack([col1, col2])
if measure:
if combo:
interaction_array = ttest_array_2col * mat_array[0,:]
ttest_array_2col = np.vstack([ttest_array_2col, interaction_array, mat_array[1:,:]])
else:
interaction_array = ttest_array_2col * mat_array
ttest_array_2col = np.vstack([ttest_array_2col, interaction_array])
measure_name = '_Int_' + measure
ttest_name_2col = ( 'TTest_'
+ group_dict[split_vars[index] + '_1']
+ group_dict[split_vars[index] + '_0']
+ measure_name
+ covar_name )
# Create a contrast array by creating a bunch of zeros
# and then put 1 and -1 at the beginning of the two
# lines
con_array_2col = np.zeros([2, ttest_array_2col.shape[0]])
template = np.array([[1,-1],[-1,1]])
con_array_2col[:,2:4] = template
else:
ttest_name_2col = ''
ttest_array_2col = np.array([])
con_array_2col = np.array([])
return ttest_name, ttest_name_2col, ttest_array, ttest_array_2col, con_array, con_array_2col
#------------------------------------------------
def create_ttests(perm_list, dir, mask, data, subs_array):
"""
This function sets up all the ttests for each comparison
Basically it looks for where the permutations have a
2 because that's used to define "IgnoreVariable".
The script then loops through all the places that there
is a 2 and generates t-tests for that variable.
"""
# Find all the places that this permutation has a 2
indices = [ i for i,x in enumerate(perm_list) if x == 2 ]
for index in indices:
# For this one you need to include having no measure
# as well as all the ones you care about
for measure in [ '' ] + measures:
# We're going to loop over all the combinations
# of the various covariates you want to control for
# eg: Age, Age_Male, Age_Male_Meds, Male, Male_Meds, Meds
for i in range(0,len(covars)+1):
for combo in it.combinations(covars, i):
if not measure in combo:
# Exclude your 999 values
data, mask = excl_999s(data, measure, combo, mask)
# Use create_arrays function to create mat and con arrays
ttest_name, ttest_name_2col, ttest_array, ttest_array_2col, con_array, con_array_2col = create_arrays_ttest(mask, data, perm_list, index, measure, combo)
# Use write_files function to save the files
write_files(subs_array, ttest_array, con_array, ttest_name, dir)
if measure:
write_files(subs_array, ttest_array_2col, con_array_2col, ttest_name_2col, dir)
#------------------------------------------------
### READ IN ARGUMENTS ###
#------------------------------------------------
try:
behav_filename= sys.argv[1]
cortisol_filename = sys.argv[2]
randomise_setup_options_file = sys.argv[3]
usable_mri_subs_filename = sys.argv[4]
output_dir = sys.argv[5]
# If there aren't enough arguments then exit the script and print
# the reason to the screen
except IndexError:
print 'EXITING - Check your input arguments'
usage()
sys.exit()
#------------------------------------------------
#------------------------------------------------
### START CODE ###
#------------------------------------------------
# Define some variables
glm_dir = os.path.join(output_dir, 'GLM')
# Set up the data columns
data = setup_data(behav_filename)
# Set up the cortisol data and merge with the data array
#data = merge_cort(data, cortisol_filename)
# Now load in the data
usable_mri_subs = np.loadtxt(usable_mri_subs_filename, dtype=str)
usable_mri_subs = np.char.replace(usable_mri_subs, 't1', '').astype(int)
#------------------------------------------------
### START CODE ###
#------------------------------------------------
# Read in the personalised options:
execfile(randomise_setup_options_file)
# Make the subs array
subs_array = make_subs_array(data)
# Mask the data so you only include people that you have mri data for
mask_all = create_mask_all(data, usable_mri_subs)
# Loop through all the different permutations of these split criteria
'''
NOTE on ITERTOOLS.PRODUCT
We're using the product command from itertools to make all the possible
combinations - rather than combinations or permutations because we're
basically nesting for loops - and this does it for us :)
'''
# Note that the range(3) here refers to the 0,1,2 codes
# for each group splitting
for perm in it.product(range(3), repeat = len(split_vars)):
# Write these names from the dictionary into a names list
'''
The j counter keeps the split_vars in the right position
and calls the appropriate values from the perm iterable
and calls the appropriate values from the perm iterable
j will always count from 0 to 2 because we've coded the
groups as 0, 1 and 2 in the dictionary
'''
names = [ group_dict[split_vars[j] + '_' + str(perm[j])] for j in range(len(split_vars)) ]
# Join the names all together
'''
'_' is the joining string
'''
group_dirname = '_'.join(name for name in names)
print group_dirname
group_dir = os.path.join(glm_dir, group_dirname)
# Now create the mask you need for this data
# Initally mask is mask_all
mask = mask_all
for j in range(len(split_vars)):
'''
I want to test the controls against the medicated patients.
So in this line I set the Control's 'Meds' value to
equal whatever the current permutation is so that they're
always included based on Medications (they may be excluded for
another reason in a different criterion!)
'''
data['Meds'][data['Depressed']==0] = perm[j]
'''
This is a pretty kickass awesome line of code that takes
advantage of list comprehensions
http://docs.python.org/2/tutorial/datastructures.html#list-comprehensions
It creates the split mask for each split_var (indexed by j)
(eg: split_vars[0] is 'Depressed') where it is equal to the
perm value (eg: if perm is (1 2 1) then the perm value for
'Depressed' (split_vars[0]) would be perm[0] --> 1)
So eg: mask asks where Depressed == 1
If the perm var is 2 it just keeps mask_all because 2 is used
to code "Ignore that measure for splitting"
'''
split_mask = np.array(data [split_vars[j]]==perm[j] if perm[j] < 2 else mask_all)
mask = mask * split_mask
# Don't do anything if the mask is empty
if mask.any():
# Create the correlation design files
create_correlations(group_dir, mask, data, subs_array)
# If you have two groups within this group then create t-tests
if 2 in list(perm):
perm_list = list(perm)
create_ttests(perm_list, group_dir, mask, data, subs_array)
'''
I'd like to include a clean up here so that folders that aren't necessary are deleted
'''
# List all the group directories that you've made
dir_list = [ os.path.join(os.path.abspath(glm_dir), d) for d in os.listdir(glm_dir) ]
# The keep names are the names for the "combined" groups
# For example these could be 'IgMed', 'IgCort', 'All' etc
keep_names = [ group_dict[key] for key in group_dict.keys() if key.endswith('2') ]
delete_list = []
# Loop through all the pairs of directories
for d1, d2 in it.combinations(dir_list, 2):
# Run the filecmp.cmpfiles command
# which compares the contents of two directories
same, diff, err = filecmp.cmpfiles(d1, d2, os.listdir(d1))
# We need to remove the matlist files because they'll be
# different just because they contain their own directory
# path name
diff_noMatlist = [ f for f in diff if 'matlist' not in f ]
# If the length of diff_noMatlist is 0 then the directories
# are identical and we want to remove one of them
if len(diff_noMatlist) == 0:
# Check to see if either of these directories are named with one
# of the "keep_names"
keep_name = list(set(keep_names) & set(d1.split('_') + d2.split('_')))
# If neither are "keep directores" then we want to remove both of them!
if len(keep_name) == 0:
delete_list.append(d1)
delete_list.append(d2)
# If only one is in "keep directories" then we want to remove the other
elif not keep_name[0] in d1:
delete_list.append(d1)
elif not keep_name[0] in d2:
delete_list.append(d2)
# Look through the list and select the unique directories
delete_list = list(set(delete_list))
# And delete them
for d in delete_list:
rmtree(d)
#------------------------------------------------
### THE END ###
# Today is April 3rd and the sun in shining in Cambridge
#------------------------------------------------