text: 
  import pandas as pd
  import numpy as np
  import json
  import requests
  import re
  import pprint
  import os
  import ndb
  from  scipy.optimize import linprog as lp
  import matplotlib.pyplot as plt
  from ipywidgets import interact
  import seaborn as sns
  
  
  class search:
  
  	punct_re = r"[^\w ]|  +"
  	se_url = 'https://api.nal.usda.gov/ndb/search?format=json&sort=n'
  	maxx = 1500
  
  	def __init__(self):
  		pass
  
  
  	def FoodNDB(self):
  
  		if self.mode == 'm':
  
  			return self.search_user
  
  
  
  		elif self.mode == 'index':
  
  			return self.index_searching
  
  
  
  	def search_user(self, food,**kwargs):
  
  		def display_results(x, scope =[0,20] ,switch = 0):
  
  
  			if switch == 0 or switch == 1:
  
  				pprint.pprint([ str(j) + ': '+i['name'] for i,j in zip(x[:20],range(scope[0],scope[1]))])
  
  			if switch == 0:
  				print ('========================================================================')
  				print ('please input the products you want following the next line ')
  				print ('For instance:')
  				print ('1. index(0,1,2,3)')
  				print ('2. more')
  				print ('3. cancel')
  
  			_input = input('>>>>> ')
  
  			if _input == 'more' and len(x) > 0:
  
  				return display_results(x[20:],[scope[1] , scope[1] + 20] ,switch = 1)
  
  			elif _input == 'cancel':
  				raise Exception("It's canceled")
  
  			elif re.search('^index\\((?:[0-9 ]+,?)*[0-9]\\)$', _input):
  
  
  				return np.array(re.sub('index|\(|\)| +','',_input).split())
  
  			else:
  				print ("unknown inputs, please try it again")
  				return display_results(x[20:], [scope[0],scope[1]],switch = 2)
  
  
  		fil_re = "UPC[^0-9a-zA-Z]+[0-9]+"
  		punct_re = r'[^\w ]|  +'
  
  		food = re.sub(punct_re," ",food.lower())
  		food_re =  '|'.join(food.split()) if len(food.split()) > 1 else food.lower()
  		if not cache_ndb._boolean_exist('ndbno',food, index = True):
  
  			data = np.array(requests.get(self.se_url , params = (('q', food),('api_key', self.api_key),('max',self.maxx))).json()['list']['item'])
  			cache_ndb._update('ndbno', {food: list(data)}, index = 0)
  		else:
  			data = np.array(cache_ndb._caches['ndbno'][0][food])
  
  
  		data = data[sorting_result(food,data)]
  
  		temp1 = display_results(data)
  
  		data = [ data[int(i)] for i in temp1]
  
  		del temp1
  
  		return data
  
  
  
  	def index_searching(self,food,index_list=[],**kwargs):
  
  
  		fil_re = "UPC[^0-9a-zA-Z]+[0-9]+"
  		punct_re = r'[^\w ]|  +'
  
  		food = re.sub(punct_re," ",food.lower())
  		food_re =  '|'.join(food.split()) if len(food.split()) > 1 else food.lower()
  		if not cache_ndb._boolean_exist('ndbno',food, index = True):
  
  			data = np.array(requests.get(self.se_url , params = (('q', food),('api_key', self.api_key),('max',self.maxx))).json()['list']['item'])
  			cache_ndb._update('ndbno', {food: list(data)}, index = 0)
  		else:
  			data = np.array(cache_ndb._caches['ndbno'][0][food])
  
  
  		data = data[sorting_result(food,data)]
  		qwe = [i for i,j in zip(data,range(len(data))) if  j == index_list[0] ]
  		
  		return qwe
  
  
  
  class NutritionDataLibrary(search):
  
  	profkey = 'inIyO1begWSRqsYtxS7m6p09PSyq7Qiw7fxzV2qN'
  	mykey = 'JPk6gFJ2IAI7YNFQuXQ7wIwUyPXTMxoKAriLzZU2'
  	mykey2 = 'qKtE5wujwySVB2sCbwQEpNkCt3r3i2BrIU23F6TL'
  	api_key = mykey2
  	nd_url = 'https://api.nal.usda.gov/ndb/V2/reports?format=json&type=b'
  	mode = 'm'
  	foods_missing_list = []
  	ndbno = []
  	ndf = []
  	foods_spreadsheet = []
  	indexS = []
  
  	diet_min = pd.read_csv('diet_minimums.csv')
  	diet_max = pd.read_csv('diet_maximums.csv')
  
  	def _helper1(x):
  		return x.replace("'", "").replace('"', "").replace(",", "").replace("&", "").replace(" ", "").replace("-","").lower()
  
  
  	def nutrition_dataframe(self, foods = None, **kwargs  ):
  
  		if 'mode' in kwargs:
  
  			self.mode = kwargs['mode']
  
  
  
  		if self.mode == 'ndbno':
  
  			if 'ndbno' in kwargs:
  				self.ndbno = kwargs['ndbno']
  
  			df = pd.DataFrame(self._retrieving_nutrition_data_ndbno(foods, **{'ndbno': self.ndbno.copy()} ),columns = ['Food Name','Nutrients','Nutritional value'], dtype = float).set_index(['Nutrients' ]).pivot(columns = 'Food Name')
  			
  			df.columns  = df.columns.droplevel(level=0)
  
  
  		else:
  			df = pd.DataFrame(self._retrieving_nutrition_data( foods,  **kwargs),columns = ['Food Name','Nutrients','Nutritional value'],dtype = float).set_index(['Nutrients' ]).pivot(columns = 'Food Name')
  			df.columns  = df.columns.droplevel(level=0)
  			print ( "[" + ", ".join(self.foods_missing_list) + ']  do/does not exist'  if len(self.foods_missing_list) != 0 else '')
  
  
  
  		self.ndf = df
  		return df
  
  
  
  	def _retrieving_nutrition_data(self, foods,**kwargs):
  		
  		def recursive_helper(foods_nd_lst):
  
  			if len(foods_nd_lst) == 0:
  				return []
  
  			return [ [foods[0],i['name'] , i['value']] for i in  foods_nd_lst[0]['food']['nutrients']]  + recursive_helper(foods_nd_lst[1:])
  
  
  
  		if len(foods) == 0:
  			return []
  
  
  
  		try:
  
  			data1 = self.FoodNDB()( foods[0],**kwargs )
  			self.ndbno += [ i['ndbno'] for i in data1]
  
  			if not cache_ndb._boolean_exist('nd',foods[0]):
  
  				data1 = requests.get(self.nd_url , params = tuple(( ('ndbno',i['ndbno']) for i in data1)) + (('api_key', self.api_key),)    ).json()
  
  				cache_ndb._update('nd', {foods[0]: data1})
  
  			else:
  
  				data1 = cache_ndb._caches['nd'][foods[0]]
  
  					
  
  			if 'index_list' in kwargs:
  				
  				kwargs.update({ 'index_list' :kwargs['index_list'][1:] })
  
  
  			
  
  			return recursive_helper(data1['foods']) + self._retrieving_nutrition_data(foods[1:] , **kwargs)
  
  
  		except KeyError:
  
  			self.foods_missing_list += [foods[0]]
  			return self._retrieving_nutrition_data(self,foods[1:] ,**kwargs)
  
  
  	def _retrieving_nutrition_data_ndbno(self,foods, ndbno = [] , **kwargs):
  
  		def recursive_helper(foods_nd_lst):
  
  			if len(foods_nd_lst) == 0:
  				return []
  
  
  			return [ [foods[0],i['name'] , i['value']] for i in  foods_nd_lst[0]['food']['nutrients']]  + recursive_helper(foods_nd_lst[1:])
  
  
  		try:
  
  			if len(foods) == 0:
  				return []
  
  			data1 = requests.get(self.nd_url , params = (('api_key', self.api_key), ('ndbno',ndbno[0])) ).json()
  
  
  
  			return recursive_helper(data1['foods']) + self._retrieving_nutrition_data_ndbno(foods[1:],ndbno[1:])
  
  		except IndexError:
  			raise IndexError("ndbno/foods list index out of range")
  
  
  class FoodLibrary(NutritionDataLibrary):
  
  	def __init__(self, foods = None, **kwargs):
  
  		for i in kwargs:
  			self.i = kwargs[i]
  
  		self.foods = foods
  
  		if 'proj2' in kwargs and kwargs['proj2'] == True:
  			self.foods_spreadsheet = 'https://docs.google.com/spreadsheets/d/e/2PACX-1vQVh0_LyaOHQdxv_iYMqJGgLVZ9qAkH0FTJBiltXTSB86KeanGtIpeghO4S09sSPyAtqlh_mHXJAV9K/pub?gid=410630770&single=true&output=csv'
  
  
  	def min_cost(self, groups=['F 19-30','M 19-30'] , **kwargs):
  
  		if len(self.ndf) == 0 or len(self.foods_spreadsheet) == 0:
  			raise Exception("Please get a nutrition dataframe/spreadsheet for food first." )
  
  		def average_of_groups(group,table):
  			if len(group) == 1:
  				return table[group[0]]
  
  
  			return table[group[0]] + average_of_groups(group[1:],table)
  
  		min_table = pd.read_csv('./diet_minimums.csv').set_index('Nutrition')
  		max_table = pd.read_csv('./diet_maximums.csv').set_index('Nutrition')
  
  		min_of_groups = average_of_groups(groups,min_table) / len(groups)
  		max_of_groups = average_of_groups(groups,max_table) / len(groups)
  
  		def test1(ndf):
  
  			ndf['NDB Quantity'] = ndf[['Quantity','Units']].T.apply(lambda x : ndb.ndb_units(x['Quantity'],x['Units']))
  
  			ndf['NDB Price'] = ndf['Price']/ndf['NDB Quantity']
  
  			return ndf
  
  		df = pd.read_csv(self.foods_spreadsheet)
  		df['Quantity'] = [float(i)for i in df['Quantity']]
  		df['Price'] = [float(i[1:])for i in df['Price']]
  
  		if 'price_delta' in kwargs:
  
  			df['Price'] = df['Price'].where(df['Food'] != kwargs['price_delta'][0], df['Price']* (1 +  (kwargs['price_delta'][1])/100)  )
  
  
  
  		df = test1(df)
  		D = self.ndf
  
  
  
  		df.dropna(how='any') 
  		Prices = df.groupby('Food')['NDB Price'].min()
  
  
  		regx1 = "[fFmM]{1}"
  		regx2 = r"[^\w ]+"
  
  		tol = 1e-6 # Numbers in solution smaller than this (in absolute value) treated as zeros
  
  		c = Prices.apply(lambda x:x.magnitude).dropna()
  
  		# Compile list that we have both prices and nutritional info for; drop if either missing
  		# Drop nutritional information for foods we don't know the price of,
  		# and replace missing nutrients with zeros.
  		Aall = D[c.index].fillna(0)
  
  
  		# Drop rows of A that we don't have constraints for.
  		Amin = Aall.loc[min_of_groups.index]
  		Amax = Aall.loc[max_table.index]
  
  		# Minimum requirements involve multiplying constraint by -1 to make <=.
  		A = pd.concat([-Amin,Amax])
  		b = pd.concat([-min_of_groups,max_of_groups]) # Note sign change for min constraints
  
  
  
  		result = lp(c, A, b, method='interior-point',options = {"presolve":False,'maxiter':5000.0})
  
  		# Put back into nice series
  		diet = pd.Series(result.x,index=c.index)
  
  
  		if 'price_delta' not in kwargs:
  			self.ot = str(result.fun)
  
  		self.result_comp = diet[diet >= tol]
  		self.result_cost = str(result.fun)
  
  
  
  
  
  		temp1 = 'the average of the (' + ', '.join(groups) + ')' if len(groups) > 1 else groups[0]
  
  		print("Cost of diet for %s is $%4.2f per day." % (temp1,result.fun))
  		print("\nYou'll be eating (in 100s of grams or milliliters):")
  		print(diet[diet >= tol])  # Drop items with quantities less than precision of calculation
  
  		tab = pd.DataFrame({"Outcome":np.abs(A).dot(diet),"Recommendation":np.abs(b)})
  
  		print("\nWith the following nutritional outcomes of interest:")
  		print(tab)
  		print("\nConstraining nutrients are:")
  
  		excess = tab.diff(axis=1).iloc[:,1]
  
  		print(excess.loc[np.abs(excess) < tol].index.tolist())
  
  
  
  
  
  	def save_nutrition_dataframe(self, Path = "./", filename = 'ndf.csv'):
  		if len(self.ndf) != 0:
  			self.ndf.to_csv(Path + filename)
  			print ('successful!')
  
  	def open_nutrition_dataframe(self, Path = "./", filename = 'ndf.csv'):
  		self.ndf = pd.read_csv(Path + filename).set_index('Nutrients')
  
  		print ('successful!')
  
  
  
  	def visualization(self,food='asparagus, raw',groups=['F 19-30','M 19-30'], range = [0,100]):
  
  
  		def f(x):
  
  			self.min_cost(groups, price_delta=[food,x])
  
  			print ('\n')
  			print ('\n')
  			print ('================================================================================================')
  
  
  			print ('The Price for '+str(food) +" changes from $" + self.ot + " to $"  + self.result_cost)
  			print ('================================================================================================')
  			fg, ax = plt.subplots(figsize=(12,9))
  
  			sns.barplot(list(self.result_comp.index),list(self.result_comp) , ax=ax)
  
  			sns.set_style("ticks", {"xtick.major.size": 8})
  			ax.set_title('Nutrients Obtained From Diet')
  			ax.set_ylabel('100s of grams or milliliters')
  			plt.show()
  
  		interact(f, x=(range[0],range[1],1));
  
  
  class cache_ndb:
  
  	if os.path.isfile('caches.json'):
  
  		with open('caches.json') as file:
  			_caches = json.load(file)
  
  	else:
  		_caches = {'ndbno':[{},{}], 'nd':{}}
  
  		with open('caches.json','w') as file:
  			json.dump(_caches,file)
  
  	def _update(type, dict_x, index = None):
  		if index == None:
  			cache_ndb._caches[type].update(dict_x)
  		else:
  			cache_ndb._caches[type][index].update(dict_x)
  
  		with open('caches.json','w') as file:
  			json.dump(cache_ndb._caches,file)
  
  
  	def _boolean_exist(type,food, index = None):
  
  		return food in cache_ndb._caches[type] if index == None else food.lower() in cache_ndb._caches[type][0] or food.lower() in cache_ndb._caches[type][1]
  
  
  
  
  def sorting_result(foodname, x):
  	regx = "(?:UPC[^0-9a-zA-Z]*[0-9]+)|(?:[^\w ]|  +)|(?:[0-9]+)"
  	foodname1 = re.sub(regx,"",foodname.lower()).split()
  	foodname1 = "|" + "|".join(foodname1) + "| +"
  
  	temp1 = np.array([ (j,re.sub(regx ,"",i['name'].lower() ), ((len(re.sub(regx+foodname1,"", i['name'].lower())))**2)**0.5 ) for i,j in zip(x,range(len(x)))], dtype=[('index', int),('name','U70') ,('leng', int)])
  	
  	temp1.sort(order='leng',kind='mergesort')
  
  	temp1 = np.append(temp1[np.char.find(temp1['name'], foodname) != -1], temp1[np.char.find(temp1['name'], foodname) == -1])
  
  
  	return temp1['index']
  
  
  abcd = [{'name':'test'},{'name':'testtest'},{'name':'tesst'},{'name':'asdaf'},{'name':'testhgdsfgdasdst'},{'name':'tesqweqwt'},{'name':'testqwt'}   ]
  #print (sorting_result('test',abcd ))
  