# References # The Onion article on the Grandito # https://www.theonion.com/taco-bells-five-ingredients-combined-in-totally-new-way-1819564909 # Ingredients of the Taco Bell menu # https://www.flashcardmachine.com/taco-bellingredients.html # Info on Taco Bell scoop and proportion sizes # https://quizlet.com/119643026/taco-bell-portioning-tool-test-flash-cards/ # Imports from inspect import getsourcefile # For getting this source file's directory from os.path import abspath # For getting this source file's directory import os import pandas as pd import numpy as np # Construct path and filenames for input and output data ThisFile = abspath(getsourcefile(lambda:0)) ThisDirectory = ThisFile.replace('EigenGrandito.py', '') SourceCsv = ThisDirectory + 'MenuIngredients.csv' def Main(): #----------------------------- # Load menu data Source_df = pd.read_csv(SourceCsv, header=0, index_col=0) Source_df = Source_df.fillna(0) # replace NaN values with 0 UnitsList = Source_df.loc[:,'Units'].to_numpy() # save the units list, for later output Source_df.drop('Units', axis=1, inplace=True) # remove unneeded columns #----------------------------- # Show A and compute the SVD A = Source_df print("\nA= ") print(A.shape) print(A) U, E, Vt = np.linalg.svd(A,full_matrices=True) #----------------------------- # negate U and V, because otherwise PC1 is all negative # and it doesn't make sense for the most dominant component to # be made up of negative amounts of ingredients. U = U*-1 Vt = Vt*-1 #----------------------------- # Optional: Analyze the singular values to determine how many principle # components would be needed to reconstruct A to a desired accuracy. if False: print("\nSingular Values = ") print(np.round(E, decimals=2)) print("\nSingular Values as percentages= ") E_percents = (E/np.sum(E))*100.0 print(np.round(E_percents, decimals=2)) iNumSingularValues = 0; TotalPercent = 0.0 TargetPercent = 80; while TotalPercent < TargetPercent and iNumSingularValues < len(E_percents): TotalPercent += E_percents[iNumSingularValues] iNumSingularValues += 1; print("\nNum Principle Components needed to recreate {:.2f} percent of A: {:d}".format(TargetPercent, iNumSingularValues)) #----------------------------- # Print U, E, Vt, and UEVt print("\nU = ") print(U.shape) print(np.round(U, decimals=2)) print("\nE = ") E = np.diag(E) print(E.shape) print(np.round(E, decimals=2)) print("\nVt = ") print(Vt.shape) print(np.round(Vt, decimals=2)) print("\nUEVt =") U = U[:,0:44] # remove the last columns of U UEVt = U @ (E @ Vt) print(np.round(UEVt, decimals=2)) #----------------------------- # prepare the grandito recipe # first principle component * first singular value Grandito = U[:,0] * E[0,0] # Assume we want at least a 1.0 amount of the 6.5 in flour tortilla # and scale everything accordingly. ScalingFactor = 1.0/Grandito[0] Grandito = Grandito * ScalingFactor # if the ingredient amount is too low, assume it's 0 for i, val in enumerate(Grandito): if val < 0.1: Grandito[i] = 0 # round to the nearest 10th Grandito = np.round(Grandito, decimals=1) # Convert the numerical values into a string, so we # can replace 0 values with a character, for better readibility strGrandito = [] for i, val in enumerate(Grandito): if Grandito[i] == 0: strGrandito.append("-") else: strGrandito.append(str(Grandito[i])) #----------------------------- # print the Eigen Grandito recipe print("\n\nBEEP BOOP BEEP BOOP...Your Eigen Grandito Recipe is:") Answer_df = pd.DataFrame(index=Source_df.index.copy()) Answer_df.insert(0, "", UnitsList, allow_duplicates=True) Answer_df.insert(1, "", strGrandito, allow_duplicates=True) print(Answer_df) #----------------------------- # Optional: Export the tables to CSVs if False: Vt = np.round(Vt, decimals=5) U = np.round(U, decimals=5) Out_df = pd.DataFrame(U) Out_df.to_csv(ThisDirectory + 'MenuIngredients_U.csv', index=False) Out_df = pd.DataFrame(Vt) Out_df.to_csv(ThisDirectory + 'MenuIngredients_Vt.csv', index=False) Main()