{"id":24954853,"url":"https://github.com/karenwky/recommendation_system_allrecipes","last_synced_at":"2025-04-10T17:29:41.524Z","repository":{"id":111777102,"uuid":"211338703","full_name":"karenwky/Recommendation_System_Allrecipes","owner":"karenwky","description":"recommending recipes with content-based filtering approach","archived":false,"fork":false,"pushed_at":"2024-09-05T13:24:57.000Z","size":8992,"stargazers_count":6,"open_issues_count":1,"forks_count":3,"subscribers_count":1,"default_branch":"master","last_synced_at":"2024-09-18T06:43:12.993Z","etag":null,"topics":["content-based-filtering","pandas","recommendation-system"],"latest_commit_sha":null,"homepage":"","language":"Jupyter Notebook","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/karenwky.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2019-09-27T14:34:14.000Z","updated_at":"2024-09-05T13:25:00.000Z","dependencies_parsed_at":"2024-09-18T06:43:19.004Z","dependency_job_id":"feb57efd-5090-40aa-8b2b-fcc7c4673d73","html_url":"https://github.com/karenwky/Recommendation_System_Allrecipes","commit_stats":null,"previous_names":["karenwky/recommendation_system_allrecipes"],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/karenwky%2FRecommendation_System_Allrecipes","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/karenwky%2FRecommendation_System_Allrecipes/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/karenwky%2FRecommendation_System_Allrecipes/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/karenwky%2FRecommendation_System_Allrecipes/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/karenwky","download_url":"https://codeload.github.com/karenwky/Recommendation_System_Allrecipes/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":236892800,"owners_count":19221239,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["content-based-filtering","pandas","recommendation-system"],"created_at":"2025-02-03T05:13:26.643Z","updated_at":"2025-02-03T05:13:27.685Z","avatar_url":"https://github.com/karenwky.png","language":"Jupyter Notebook","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Recommendation System: Allrecipes.com\nRecommending recipes with content-based filtering approach by feature extraction (nutrition values). \n\n## Data Source\nData from [Kaggle](https://www.kaggle.com/elisaxxygao/foodrecsysv1) user elisaxxygao, containing two sets of data about user interaction information and recipe information, recipe images are also provided.\n\n## Feature Extraction\n\u003cimg src=\"/images/nutrition_table.png\" alt=\"nutrition_table\" width=550\u003e \u003cbr/\u003e\nFor comparing similarity between recipes, 7 nutritions are selected and daily percent values are extracted which are based on a 2,000 calorie diet. \n\n## Distance Calculation Methods\nAfter doing normalization for the nutrition data, three distance calculation methods are applied to experiment Top 3 recommendation results. \n\n\u003cimg src=\"/images/selected_recipe_222388.png\" alt=\"selected_recipe_222388\" width=450\u003e \u003cbr/\u003e\n\n1. Cosine Distance \u003cbr/\u003e\n   ![cosine_222388](/images/cosine_222388.png)\n   \n2. Euclidean Distance \u003cbr/\u003e\n   ![euclidean_222388](/images/euclidean_222388.png)\n   \n3. Hamming Distance \u003cbr/\u003e\n   ![hamming_222388](/images/hamming_222388.png)\n\nNutrition data are similar and different results are generated by various distance calculation methods. Thus, a hybrid recommender is created to integrate recommendations from three approaches. \n\n## Hybrid Recommender\n```python\n\"\"\"\nHybrid Nutrition Recommender which integrates Top 2 recommendations from 3 different distance approaches \n(cosine, euclidean, hamming) and sort the results by selected criteria(s)\n\ndf_normalized: normalized nutrition data\nrecipe_id: find similar recipes based on the selected recipe\nsort_order: must be in list, 4 options available: ['aver_rate'], ['review_nums'], ['aver_rate', 'review_nums'], ['review_nums', 'aver_rate']\nN: Top N recipe(s)\n\nreturn 1) recipe id, recipe name and image of Top N recommendation, \n2) nutrition data of selected recipe and Top N recommendation,\n3) average rating and number of review of Top N recommendation\n\"\"\"\n\ndef nutrition_hybrid_recommender(recipe_id, sort_order, N):\n    start = time()\n    \n    allRecipes_cosine = pd.DataFrame(df_normalized.index)\n    allRecipes_cosine = allRecipes_cosine[allRecipes_cosine.recipe_id != recipe_id]\n    allRecipes_cosine[\"distance\"] = allRecipes_cosine[\"recipe_id\"].apply(lambda x: cosine(df_normalized.loc[recipe_id], df_normalized.loc[x]))\n    \n    allRecipes_euclidean = pd.DataFrame(df_normalized.index)\n    allRecipes_euclidean = allRecipes_euclidean[allRecipes_euclidean.recipe_id != recipe_id]\n    allRecipes_euclidean[\"distance\"] = allRecipes_euclidean[\"recipe_id\"].apply(lambda x: euclidean(df_normalized.loc[recipe_id], df_normalized.loc[x]))\n    \n    allRecipes_hamming = pd.DataFrame(df_normalized.index)\n    allRecipes_hamming = allRecipes_hamming[allRecipes_hamming.recipe_id != recipe_id]\n    allRecipes_hamming[\"distance\"] = allRecipes_hamming[\"recipe_id\"].apply(lambda x: hamming(df_normalized.loc[recipe_id], df_normalized.loc[x]))\n    \n    Top2Recommendation_cosine = allRecipes_cosine.sort_values([\"distance\"]).head(2).sort_values(by=['distance', 'recipe_id'])\n    Top2Recommendation_euclidean = allRecipes_euclidean.sort_values([\"distance\"]).head(2).sort_values(by=['distance', 'recipe_id'])\n    Top2Recommendation_hamming = allRecipes_hamming.sort_values([\"distance\"]).head(2).sort_values(by=['distance', 'recipe_id'])\n    \n    recipe_df = recipe.set_index('recipe_id')\n    hybrid_Top6Recommendation = pd.concat([Top2Recommendation_cosine, Top2Recommendation_euclidean, Top2Recommendation_hamming])\n    aver_rate_list = []\n    review_nums_list = []\n    for recipeid in hybrid_Top6Recommendation.recipe_id:\n        aver_rate_list.append(recipe_df.at[recipeid, 'aver_rate'])\n        review_nums_list.append(recipe_df.at[recipeid, 'review_nums'])\n    hybrid_Top6Recommendation['aver_rate'] = aver_rate_list\n    hybrid_Top6Recommendation['review_nums'] = review_nums_list\n    TopNRecommendation = hybrid_Top6Recommendation.sort_values(by=sort_order, ascending=False).head(N).drop(columns=['distance'])\n    \n    recipe_id = [recipe_id]   \n    recipe_list = []\n    image_list = []\n    image_path = \"./foodrecsysv1/raw-data-images/{}.jpg\"\n    for recipeid in TopNRecommendation.recipe_id:\n        recipe_id.append(recipeid)   # list of recipe id of selected recipe and recommended recipe(s)\n        recipe_list.append(\"{}  {}\".format(recipeid, recipe_df.at[recipeid, 'recipe_name']))\n        image_list.append(image_path.format(recipeid))\n    \n    image_array = []\n    for imagepath in image_list:\n        img = image.load_img(imagepath)\n        img = image.img_to_array(img, dtype='int')\n        image_array.append(img)\n        \n    fig = plt.figure(figsize=(15,15))\n    gs1 = gridspec.GridSpec(1, N)\n    axs = []\n    for x in range(N):\n        axs.append(fig.add_subplot(gs1[x]))\n        axs[-1].imshow(image_array[x])\n    [axi.set_axis_off() for axi in axs]\n    for axi, x in zip(axs, recipe_list):\n        axi.set_title(x)\n    \n    end = time()\n    running_time = end - start\n    print('time cost: %.5f sec' %running_time)\n    return df_normalized.loc[recipe_id, :], TopNRecommendation\n```\n\n\u003cimg src=\"/images/selected_recipe_222886.png\" alt=\"selected_recipe_222886\" width=450\u003e \u003cbr/\u003e\n\n1. Sort by average rating\n   ![hybrid_ar](/images/hybrid_ar.png) \u003cbr/\u003e\n   \u003cimg src=\"/images/nutrition_ar.png\" alt=\"nutrition_ar\" width=550\u003e \u003cbr/\u003e\n   \u003cimg src=\"/images/topN_ar.png\" alt=\"topN_ar\" width=400\u003e \n   \n2. Sort by number of reviews\n   ![hybrid_rn](/images/hybrid_rn.png) \u003cbr/\u003e\n   \u003cimg src=\"/images/nutrition_rn.png\" alt=\"nutrition_rn\" width=550\u003e \u003cbr/\u003e\n   \u003cimg src=\"/images/topN_rn.png\" alt=\"topN_rn\" width=400\u003e \n     \nAverage rating and number of reviews are different popularity standards, and with these two sorting criterias similar results are generated. It is surprised that with nutrition information, even alcohol recipes can be detected and recommended. \n\n## Deployment\n\u003cimg src=\"/images/hybrid_deploy.gif\" alt=\"hybrid_deploy\" width=600\u003e \u003cbr/\u003e\nIntegrate Top 10 recommendation from three distance calculation approaches, then generate Top N recommendation sorted by various criterias, e.g. average rating or number of reviews\n\n## Detailed Presentation\n* Check out complete workflow with [Jupyter Notebook](./code).\n* Check out complete code of [Flask Deployment](./flask_deployment).\n\n## Skills Acquired\n* Pandas: feature extraction, data cleaning and data imputation\n* Keras: image processing (process image files to array and show them according to recommended recipes)\n* Matplotlib: using GridSpec to do subplots visualization within a for loop\n* Flask: deployment of recommender engine into web application\n\n## Acknowledgements\nSubplots code reference from Stack Overflow user [armatita](https://stackoverflow.com/questions/46713186/matplotlib-loop-make-subplot-for-each-category?rq=1) and [Nirmal](https://stackoverflow.com/questions/25862026/turn-off-axes-in-subplots). Thank you coders for sharing your experience! =]\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fkarenwky%2Frecommendation_system_allrecipes","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fkarenwky%2Frecommendation_system_allrecipes","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fkarenwky%2Frecommendation_system_allrecipes/lists"}