''' Created on Oct 28, 2018 Finished on March 24th, 2019 @author: Brett Paufler Copyright Brett Paufler One time disposable script for sleep_studies webpage Much of the logic cut and pasted from calorie_counter Works One Time Script ''' #Default Values dir_in = './/input//' dir_out = './/output' file_in = './/input//sleep-studies-data.txt' #Extract Text from File with open(file_in, 'r') as f: raw_text = f.read() #print raw_text data_text = raw_text.split('##########')[-1] #print data_text text_list = [d for d in data_text.split('\n') if d and d != '?'] #print text_list #print len(text_list) #print text_list.pop() #Object to Hold Each Data Entry class DataPoint(): def __init__(self, month, date, hour, minute, fuzz, start): #Many Bugs Originated Here #Beware of Magic Numbers #Magic Numbers if month == 12: if date > 19: date -= 2 #two missing days make up day = date - 15.0 #started on the 15th if month == 1: day = date + 16.0 - 2.0 #Seems to Work #Time is hours from start self.time = ( 24.0 * day + hour #/ 24.0 + minute / 60.0 #1440.0 ) self.fuzz = fuzz / 60.0 self.start = start #True is Sleep Starts def __repr__(self): text = 'DataPoint\n%.5f\n%.5f\n%s' % ( self.time, self.fuzz, self.start) return text #Convert Text to List of Data Points data_points=[] for text in text_list: #print text #1-7/6:45-15 if '+' in text: start = True else: start = False #print start #Same Symbol Makes the Split Easier text = text.replace('+', '-') text = text.replace('/', '-') text = text.replace(':', '-') #print text #Split on '=', assert length 5 d = text.split('-') assert len(d) == 5 #print d #Assign Default Value if not d[4]: d[4] = 0 #print d #Cast to Ints d = [int(v) for v in d] #print d #This is more explicit that *d month, date, hour, minute, fuzz = d DP = DataPoint( month, date, hour, minute, fuzz, start) #print DP data_points.append(DP) class Slumber(): def __init__(self, sleep, wake): self.begin_fuzz = sleep.time self.begin = sleep.time + sleep.fuzz self.end = wake.time - wake.fuzz self.end_fuzz = wake.time #This is the serious debugging #print sleep.time / 24 #print self.end - self.begin assert 0 <= (self.end - self.begin) < 12 assert self.begin_fuzz <= self.begin assert self.end <= self.end_fuzz def __repr__(self): text = 'Slumber\t%.3f\n\t%.3f\n\t%.3f\n\t%.3f' % ( self.begin_fuzz, self.begin, self.end, self.end_fuzz) return text #Converts to a listing of Slumber Events slumber_values = [] for i in range(len(data_points)/2): #Pairing Data by Sleep/Wake Cycle sleep = data_points.pop() wake = data_points.pop() assert sleep.start == True assert wake.start == False #Listing of SLeep Cycles #print i #print sleep #print wake S = Slumber(sleep, wake) slumber_values.append(S) #print S ################### # ANALYSIS ################### #Numerical Analysis def first_analysis(): data_points = len(slumber_values) print 'DATA POINTS: %d' % data_points total_time = max([s.end for s in slumber_values]) print 'TOTAL TIME: %.4f' % total_time days = total_time / 24.0 print 'DAYS: %.4f' % days total_begin_fuzz = sum([s.begin - s.begin_fuzz for s in slumber_values]) print 'TOTAL BEGIN FUZZ: %.4f' % total_begin_fuzz total_end_fuzz = sum([s.end_fuzz - s.end for s in slumber_values]) print 'TOTAL END FUZZ: %.4f' % total_end_fuzz #This was a major line item in the debuggin process #All values should be positive sleep_times = [s.end - s.begin for s in slumber_values] #Note, this is duplicate at this point for st in sleep_times: assert st >= 0 assert st <= 12 print sleep_times total_sleep_time = sum(sleep_times) average_sleep_time = total_sleep_time / days print 'TOTAL SLEEP TIME: %.4f' % total_sleep_time print 'AVERAGE SLEEP TIME: %.4f' % average_sleep_time #This gets tricky, as I recorded some nights #As two or more sleeping shifts for nap in range(1,6): no_naps_sleep_times = [s for s in sleep_times if s > nap] print 'NO NAPS(<%d)=(%d): %.4f (ave length)' % ( nap, len(no_naps_sleep_times), sum(no_naps_sleep_times) / len(no_naps_sleep_times)) #first_analysis() ''' DATA POINTS: 46 TOTAL TIME: 559.0000 DAYS: 23.2917 TOTAL BEGIN FUZZ: 7.3000 TOTAL END FUZZ: 13.6333 [5.616666666666667, 1.1833333333333336, 8.916666666666664, 0.25, 0.7666666666666657, 10.0, 0.5, 9.049999999999997, 8.166666666666671, 1.0, 7.5, 5.966666666666669, 0.46666666666664014, 0.46666666666666856, 1.1500000000000057, 7.966666666666669, 8.800000000000011, 8.75, 0.8333333333333144, 7.0, 2.183333333333337, 1.0166666666666515, 9.833333333333314, 5.7333333333333485, 2.6666666666666856, 9.900000000000034, 6.68333333333328, 4.2333333333333485, 3.5166666666666515, 8.25, 1.0, 7.616666666666674, 4.25, 1.5, 1.6666666666666856, 0.5, 8.75, 0.0, 8.0, 0.0, 0.0, 0.25, 8.5, 0.0, 0.25, 8.5] TOTAL SLEEP TIME: 199.1500 AVERAGE SLEEP TIME: 8.5503 NO NAPS(<1)=(31): 6.2215 (ave length) NO NAPS(<2)=(26): 7.1673 (ave length) NO NAPS(<3)=(24): 7.5625 (ave length) NO NAPS(<4)=(23): 7.7384 (ave length) NO NAPS(<5)=(21): 8.0714 (ave length) Clearly, I broke some nights up into two shifts Naps roughly equal one a day More importantly, all values are reasonable So, moving on ''' ######################## # Better Data ######################## sleeps = [] fuzzs = [] def _wrapping_values(one, two): '''Values Wrapping Two Dates Are Given Two Values (Begin-24, 0-End)''' one_day, one = divmod(one, 24) two_day, two = divmod(two, 24) if one_day == two_day: return [(one_day, one, two)] else: return [(one_day, one, 24.0), (two_day, 0.00, two)] for s in slumber_values: #For Visual Test #print s.begin_fuzz, s.begin, s.end, s.end_fuzz #print divmod(s.end_fuzz, 24) fuzzs += ( _wrapping_values( s.begin_fuzz, s.begin)) sleeps += ( _wrapping_values( s.begin, s.end)) fuzzs += ( _wrapping_values( s.end, s.end_fuzz)) #print fuzzs #print sleeps #Kills Zero Values fuzzs = [(a, b, c) for a, b, c in fuzzs if b != c] sleeps = [(a, b, c) for a, b, c in sleeps if b != c] print fuzzs print sleeps #fuzz_low = [(a, b, c) for a, b, c in fuzzs if b < c] #fuzz_high = [(a, b, c) for a, b, c in fuzzs if b > c] #sleep_low = [(a, b, c) for a, b, c in sleeps if b < c] #sleep_high = [(a, b, c) for a, b, c in sleeps if b > c] ''' This Section Exports Three Tuples of the Form (day, begin_time, end_time) fuzzs sleeps All times are on the same day ''' ################################################### # Graphing ################################################### import matplotlib.pyplot as plt #Bar Graph Needs Difference for 'bottom' kwarg bar_fuzzs = [(a, b, c-b) for a, b, c in fuzzs if b != c] bar_sleeps = [(a, b, c-b) for a, b, c in sleeps if b != c] f_day, f_start, f_finish = zip(*bar_fuzzs) s_day, s_start, s_finish = zip(*bar_sleeps) print f_day, f_start, f_finish print s_day, s_start, s_finish print max(f_start), max(f_finish), fig, ax = plt.subplots(figsize=(10,5)) ax.bar(f_day, f_finish, bottom=f_start, color='gray') ax.bar(s_day, s_finish, bottom=s_start, color='black') plt.ylim(ymin=0, ymax=24) plt.xlim(xmin=0, xmax=24) plt.yticks(range(0,26,2)) plt.title('Sleep Studies') plt.xlabel('Days In Study') plt.ylabel('24hr Clock') #plt.show() plt.savefig('./output/sleep_studies_bar.png') plt.close() #Black is Time STATUS_SLEEPING #GRAY is Fuzzier Time #