Macronutrient Triangle by Charles M. Greenspon Clone
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 

199 lines
8.7 KiB

  1. ### 2D Macro Comparison
  2. ### Charles M. Greenspon - charles.greenspon@gmail.com
  3. # Import Pandas & numpy
  4. import numpy as np
  5. import pandas as pd
  6. # Import Bokeh
  7. from bokeh.plotting import figure, show, output_file
  8. from bokeh.layouts import layout, widgetbox, column, row, Spacer
  9. from bokeh.embed import file_html
  10. from bokeh.resources import CDN
  11. from bokeh.io import show, export_png
  12. from bokeh.models import Text, CDSView, Plot, Circle, CustomJS, CustomJSFilter, HoverTool, ColumnDataSource, Select, CategoricalColorMapper, TextInput
  13. from bokeh.transform import factor_cmap
  14. from bokeh.models.widgets import Toggle
  15. from bokeh.events import ButtonClick
  16. output_file("2D_MacroCompare.html", title="Macronutrient Comparison")
  17. source_colors = pd.DataFrame()
  18. source_colors['reg'] = ['#4CAF50', '#FF9800', '#9C27B0', '#8D6E63', '#f44336', '#2196F3'] # Regular colors
  19. source_colors['cb'] = ['#673AB7', '#FF5722', '#FFC107', '#3F51B5', '#f44336', '#2196F3']
  20. # Prepare Data - Edit these columns if different/more/less are desired
  21. target_cols = ['Food Group', 'Food Name', 'Protein (g)', 'Fat (g)', 'Carbohydrates (g)', 'Calories', 'Water (g)', 'Sugar (g)', 'Fiber (g)']
  22. og_data = pd.read_excel("USDA-SR28-V1.xlsx", skiprows = 2, header = 1, usecols = target_cols)
  23. groups_of_interest = ['Dairy and Egg Products','Beef Products', 'Breakfast Cereals',
  24. 'Cereal Grains and Pasta', 'Finfish and Shellfish Products',
  25. 'Fruits and Fruit Juices', 'Lamb, Veal, and Game Products',
  26. 'Legumes and Legume Products', 'Nut and Seed Products',
  27. 'Pork Products', 'Poultry Products', 'Sausages and Luncheon Meats',
  28. 'Vegetables and Vegetable Products']
  29. trunc_data = og_data.loc[og_data['Food Group'].isin(groups_of_interest)]
  30. trunc_data = trunc_data.reset_index(drop=True)
  31. ### Data Preparation
  32. output_groups = ['Fruit & Vegetables', 'Cereals & Grains', 'Animal Products',
  33. 'Nuts & Seeds', 'Meat', 'Fish']
  34. meta_groups = []
  35. meta_groups.append(['Fruits and Fruit Juices', 'Legumes and Legume Products',
  36. 'Vegetables and Vegetable Products']) # Fruit & Veg
  37. meta_groups.append(['Breakfast Cereals', 'Cereal Grains and Pasta']) # Cereals & Grains
  38. meta_groups.append(['Dairy and Egg Products']) # Animal Products
  39. meta_groups.append(['Nut and Seed Products']) # Nuts & Seeds
  40. meta_groups.append(['Beef Products', 'Lamb, Veal, and Game Products', 'Pork Products',
  41. 'Poultry Products', 'Sausages and Luncheon Meats']) # Meat
  42. meta_groups.append(['Finfish and Shellfish Products']) # Fish
  43. trunc_data['Meta Group'] = ""
  44. trunc_data['def_colors'] = ""
  45. trunc_data['cb_colors'] = ""
  46. for g in range(len(output_groups)):
  47. trunc_data["Meta Group"].loc[trunc_data["Food Group"].str.contains('|'.join(meta_groups[g]))] = output_groups[g]
  48. trunc_data["def_colors"].loc[trunc_data["Food Group"].str.contains('|'.join(meta_groups[g]))] = source_colors.loc[g,'reg']
  49. trunc_data["cb_colors"].loc[trunc_data["Food Group"].str.contains('|'.join(meta_groups[g]))] = source_colors.loc[g,'cb']
  50. trunc_data['x'] = trunc_data['Calories']
  51. trunc_data['y'] = trunc_data['Protein (g)']
  52. trunc_data['color'] = trunc_data["def_colors"]
  53. # Prepare the legend
  54. legend_x = np.ones(len(output_groups)) * 1000
  55. legend_y = np.linspace(100,80,6)
  56. legend_c = {'reg': source_colors['reg'].to_list(), 'cb': source_colors['cb'].to_list()}
  57. legend_source = ColumnDataSource(dict(x=legend_x, y=legend_y, text=output_groups, colors=legend_c['reg']))
  58. legend_glyph = Text(x="x", y="y", text="text", text_align="right", text_color="colors", text_font_size = "10pt")
  59. ### Search filter
  60. unfilt_source = ColumnDataSource(trunc_data)
  61. filterDataIndices = dict(x=[True for num in range(1, len(unfilt_source.data['Food Name']))])
  62. filtered_source = ColumnDataSource(filterDataIndices)
  63. callback = CustomJS(args=dict(source=filtered_source, sel=unfilt_source), code="""
  64. var textTyped = cb_obj.value.toUpperCase();
  65. var filteredDataSource = source.data['x'];
  66. for (var i = 0; i < sel.get_length(); i++){
  67. if (sel.data['Food Name'][i].toUpperCase().includes(textTyped)){
  68. filteredDataSource[i] = true;
  69. } else if (sel.data['Food Group'][i].toUpperCase().includes(textTyped)){
  70. filteredDataSource[i] = true;
  71. } else {
  72. filteredDataSource[i] = false;
  73. }
  74. }
  75. source.change.emit();
  76. sel.change.emit();
  77. """)
  78. food_search = TextInput(value="", name='foodSearchTxtInput', title="Food Search:", callback=callback, width=200)
  79. custom_filter = CustomJSFilter(args=dict(source=unfilt_source, sel=filtered_source), code='''
  80. return sel.data['x'];
  81. ''')
  82. view = CDSView(source=unfilt_source, filters=[custom_filter])
  83. # Colorblind toggle
  84. clr_callback = CustomJS(args=dict(source=unfilt_source, color_source=legend_c,
  85. leg_source=legend_source), code="""
  86. var state = cb_obj.active
  87. var data = source.data
  88. var leg = leg_source.data
  89. if (state){
  90. data['color'] = data['cb_colors'];
  91. leg['colors'] = color_source['cb'];
  92. } else{
  93. data['color'] = data['def_colors'];
  94. leg['colors'] = color_source['reg'];
  95. }
  96. source.change.emit();
  97. leg_source.change.emit();
  98. """)
  99. color_toggle = Toggle(label="Colorblind Mode", button_type="success", callback = clr_callback, width=200)
  100. ### Plotting
  101. p_tools = "hover, wheel_zoom, box_zoom, reset"
  102. p = figure(title = "Amount per 100g", tools = p_tools, plot_width=900,
  103. x_range=(-50, 1050), x_axis_label='Calories',
  104. y_range=(-5,105), y_axis_label='Protein (g)', sizing_mode='scale_width')
  105. p.circle(x = 'x', y = 'y', size = 7, color = 'color', alpha = 0.3,
  106. source = unfilt_source, view=view, name = 'dp')
  107. p.add_glyph(legend_source, legend_glyph)
  108. p.xgrid.grid_line_color = None
  109. p.ygrid.grid_line_color = None
  110. p.hover.tooltips = [
  111. ("Name", "@{Food Name}")]
  112. p.hover.names = ['dp']
  113. ### Make X/Y selection
  114. callback_x = CustomJS(args=dict(source=unfilt_source, ax_lab=p.xaxis[0], ax_range=p.x_range,
  115. leg_source=legend_source), code="""
  116. var selected_column = cb_obj.value
  117. // Set data
  118. var data = source.data;
  119. data['x'] = data[cb_obj.value];
  120. var leg_x = leg_source.data['x']
  121. // Edit axis label
  122. ax_lab.axis_label = cb_obj.value
  123. // Edit ranges
  124. if (cb_obj.value.includes('(g)')){
  125. ax_range.start = -5;
  126. ax_range.end = 105;
  127. for (var i = 0; i < 6; i++){
  128. leg_x[i] = 100;
  129. }
  130. } else {
  131. ax_range.start = -50;
  132. ax_range.end = 1050;
  133. for (var i = 0; i < 6; i++){
  134. leg_x[i] = 1000;
  135. }
  136. }
  137. // Push changes
  138. source.change.emit();
  139. ax_lab.change.emit();
  140. ax_range.change.emit();
  141. leg_source.change.emit();
  142. """)
  143. select_x = Select(title="X-Axis:", value="Calories", options=target_cols[2:len(target_cols)], callback=callback_x, width=200)
  144. callback_y = CustomJS(args=dict(source=unfilt_source, ax_lab=p.yaxis[0], ax_range=p.y_range,
  145. leg_source=legend_source, y_vals=legend_y), code="""
  146. var selected_column = cb_obj.value
  147. // Set data
  148. var data = source.data;
  149. data['y'] = data[cb_obj.value];
  150. var leg_y = leg_source.data['y']
  151. // Edit axis label
  152. ax_lab.axis_label = cb_obj.value
  153. // Edit axis range
  154. if (cb_obj.value.includes('(g)')){
  155. ax_range.start = -5;
  156. ax_range.end = 105;
  157. for (var i = 0; i < 6; i++){
  158. leg_y[i] = y_vals[i];
  159. }
  160. } else {
  161. ax_range.start = -50;
  162. ax_range.end = 1050;
  163. for (var i = 0; i < 6; i++){
  164. leg_y[i] = y_vals[i] * 10;
  165. }
  166. }
  167. // Push changes
  168. source.change.emit();
  169. ax_lab.change.emit();
  170. ax_range.change.emit();
  171. leg_source.change.emit();
  172. """)
  173. select_y = Select(title="Y-Axis:", value="Protein (g)", options=target_cols[2:len(target_cols)], callback=callback_y, width=200)
  174. # Go
  175. #show(row(column(food_search, select_x, select_y,color_toggle, inf) ,p))
  176. show(column(row(Spacer(width=50),food_search, select_x, select_y,Spacer(width=20)) , row(p, Spacer(width=20)), row(Spacer(width=50),color_toggle,Spacer(width=500))))
  177. #export_png(p, filename="plot.png")