Skip to content

Instantly share code, notes, and snippets.

@yarox
Last active February 13, 2023 19:21
Show Gist options
  • Save yarox/c4b8517b3a933f1195ee2126d1dab141 to your computer and use it in GitHub Desktop.
Save yarox/c4b8517b3a933f1195ee2126d1dab141 to your computer and use it in GitHub Desktop.
Display the source blob
Display the rendered blob
Raw
{
"metadata": {
"language_info": {
"codemirror_mode": {
"name": "python",
"version": 3
},
"file_extension": ".py",
"mimetype": "text/x-python",
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.8"
},
"kernelspec": {
"name": "python",
"display_name": "Python (Pyodide)",
"language": "python"
}
},
"nbformat_minor": 4,
"nbformat": 4,
"cells": [
{
"cell_type": "markdown",
"source": "# Content-based Recommender System PoC\n\nThe global task of the recommendation system is to select a shortlist of content from a large catalog that is most suitable for a particular user. \n\nSimple recommenders offer generalized recommendations to every user, based on movie popularity and/or genre. The basic idea behind this system is that movies that are more popular and critically acclaimed will have a higher probability of being liked by the average audience. An example could be IMDB Top 250.\n\nContent-based recommendation systems recommend items to a user by using the similarity of items. This recommender system recommends products or items based on their description or features. It identifies the similarity between the products based on their descriptions. It also considers the user's previous history in order to recommend a similar product. Note that the recommendations are specific to this user, as the model did not use any information about other users.\n\nTo address some of the limitations of content-based filtering, collaborative filtering uses similarities between users and items simultaneously to provide recommendations. This allows for serendipitous recommendations; that is, collaborative filtering models can recommend an item to user A based on the interests of a similar user B.\n\nRecommender Systems depend heavily on the ability to compare multiple parameters of a large pool of candidates and identify the ones that best match the required characteristics. Comparing parameters when they are numeric is fairly straightforward. But a lot of characteristics tend to be _categorical variables_ that take on a value from a limited, usually fixed, set of values. As an example, the categorical variable, ‘eye color’ can be blue, brown, amber, hazel, green and grey. Comparing categorical variables can be tricky because it is not easy to map these variables onto a numeric model that represents them accurately.\n\nAnother kind of parameters are textual. For this case, we need to extract some kind of features from the text data before we can compute the similarity between them because it is not possible to do so between any two given texts in their raw forms. To do this, we need to compute the Term Frequency-Inverse Document Frequency (TF-IDF) vectors. In its essence, the TF-IDF score is the frequency of a word occurring in a document, down-weighted by the number of documents in which it occurs.\n\nThe recommender system we are going to implement is content based and will take into account title (textual), teacher (categorical), categories (categorical), areas (categorical), and softwares (categorical) from a course to make the recommendations.\n",
"metadata": {}
},
{
"cell_type": "code",
"source": "import pandas as pd\nimport numpy as np\n\nfrom sklearn.feature_extraction.text import TfidfVectorizer\nfrom sklearn.metrics.pairwise import linear_kernel\nfrom sklearn.preprocessing import Normalizer",
"metadata": {
"trusted": true
},
"execution_count": 1,
"outputs": []
},
{
"cell_type": "markdown",
"source": "## Get the data",
"metadata": {}
},
{
"cell_type": "markdown",
"source": "Query the database and export the results to a csv file\n\n```SQL\nSELECT \n\tc.id, \n\tct.title AS course_title, \n\tu.permalink AS teacher,\n\tGROUP_CONCAT(DISTINCT at2.name ORDER BY at2.name ASC SEPARATOR ', ') AS areas,\n\tGROUP_CONCAT(DISTINCT ct2.name ORDER BY ct2.name ASC SEPARATOR ', ') AS categories,\n\tGROUP_CONCAT(DISTINCT st2.name ORDER BY st2.name ASC SEPARATOR ', ') AS softwares\nFROM courses c \nLEFT JOIN users u ON u.id = c.user_id \nLEFT JOIN course_translations ct ON c.id = ct.course_id\nLEFT JOIN area_relationships ar ON ar.target_id = c.id \nLEFT JOIN area_translations at2 ON at2.area_id = ar.area_id \nLEFT JOIN category_relationships cr ON cr.target_id = c.id \nLEFT JOIN category_translations ct2 ON ct2.category_id = cr.category_id \nLEFT JOIN software_relationships sr ON sr.target_id = c.id \nLEFT JOIN softwares st2 ON st2.id = sr.software_id \nWHERE c.aasm_state = 'published' AND \n ct.locale = 'en' AND\n\t at2.locale = 'en' AND \n\t ct2.locale = 'en'\nGROUP BY c.id\n```",
"metadata": {}
},
{
"cell_type": "code",
"source": "courses = pd.read_csv('./courses.csv')\ncourses.head(10)",
"metadata": {
"trusted": true
},
"execution_count": 5,
"outputs": [
{
"execution_count": 5,
"output_type": "execute_result",
"data": {
"text/plain": " id title teacher \\\n0 1 Illustration of a Pin-Up with Traditional Tech... fernando_vicente \n1 2 Stop Motion For Fun! duprez \n2 5 By Hand and On The Cover byjimenez \n3 6 Build a Friend: From Pencil to Movement zenzuke \n4 7 Calligraphy and Rock’n’Roll ivancastro \n5 8 Hollywood Retouching peterporta \n6 9 Reasons to Repeat moniquilla \n7 10 Illustrate with Confidence and you will Triumph conspiracystudio \n8 11 Design of Pictograms rfaura \n9 12 Illustrating Time stereoplastika \n\n areas \\\n0 3D,Animation,Character Design,Drawing,Film, Vi... \n1 Animation,Design,Film,Film, Video & TV,Filmmak... \n2 3D,Accessory Design,Advertising,Arts & Crafts,... \n3 2D Animation,Animation,Character Animation,Cha... \n4 Animation,Brush Painting,Calligraphy,Calligrap... \n5 Photo Retouching,Photography,Photography Post-... \n6 Advertising,Art Direction,Branding & Identity,... \n7 Advertising,Design,Illustration,Vector Illustr... \n8 Design,Graphic Design,Icon Design,Photography,... \n9 Advertising,Digital Design,Film Title Design,F... \n\n categories \\\n0 Illustration,Web & App Design \n1 3D & Animation,Architecture & Spaces,Calligrap... \n2 3D & Animation,Architecture & Spaces,Calligrap... \n3 3D & Animation,Photography & Video \n4 3D & Animation,Calligraphy & Typography,Design... \n5 Photography & Video \n6 Design,Illustration,Marketing & Business,Web &... \n7 Design,Illustration \n8 3D & Animation,Calligraphy & Typography,Design \n9 3D & Animation,Design,Illustration,Marketing &... \n\n softwares \n0 NaN \n1 iMovie \n2 NaN \n3 Adobe After Effects \n4 Adobe Illustrator \n5 Adobe Photoshop \n6 NaN \n7 Adobe Illustrator,Adobe Photoshop \n8 Adobe Freehand,Adobe Illustrator,Adobe InDesign \n9 Adobe Illustrator ",
"text/html": "<div>\n<style scoped>\n .dataframe tbody tr th:only-of-type {\n vertical-align: middle;\n }\n\n .dataframe tbody tr th {\n vertical-align: top;\n }\n\n .dataframe thead th {\n text-align: right;\n }\n</style>\n<table border=\"1\" class=\"dataframe\">\n <thead>\n <tr style=\"text-align: right;\">\n <th></th>\n <th>id</th>\n <th>title</th>\n <th>teacher</th>\n <th>areas</th>\n <th>categories</th>\n <th>softwares</th>\n </tr>\n </thead>\n <tbody>\n <tr>\n <th>0</th>\n <td>1</td>\n <td>Illustration of a Pin-Up with Traditional Tech...</td>\n <td>fernando_vicente</td>\n <td>3D,Animation,Character Design,Drawing,Film, Vi...</td>\n <td>Illustration,Web &amp; App Design</td>\n <td>NaN</td>\n </tr>\n <tr>\n <th>1</th>\n <td>2</td>\n <td>Stop Motion For Fun!</td>\n <td>duprez</td>\n <td>Animation,Design,Film,Film, Video &amp; TV,Filmmak...</td>\n <td>3D &amp; Animation,Architecture &amp; Spaces,Calligrap...</td>\n <td>iMovie</td>\n </tr>\n <tr>\n <th>2</th>\n <td>5</td>\n <td>By Hand and On The Cover</td>\n <td>byjimenez</td>\n <td>3D,Accessory Design,Advertising,Arts &amp; Crafts,...</td>\n <td>3D &amp; Animation,Architecture &amp; Spaces,Calligrap...</td>\n <td>NaN</td>\n </tr>\n <tr>\n <th>3</th>\n <td>6</td>\n <td>Build a Friend: From Pencil to Movement</td>\n <td>zenzuke</td>\n <td>2D Animation,Animation,Character Animation,Cha...</td>\n <td>3D &amp; Animation,Photography &amp; Video</td>\n <td>Adobe After Effects</td>\n </tr>\n <tr>\n <th>4</th>\n <td>7</td>\n <td>Calligraphy and Rock’n’Roll</td>\n <td>ivancastro</td>\n <td>Animation,Brush Painting,Calligraphy,Calligrap...</td>\n <td>3D &amp; Animation,Calligraphy &amp; Typography,Design...</td>\n <td>Adobe Illustrator</td>\n </tr>\n <tr>\n <th>5</th>\n <td>8</td>\n <td>Hollywood Retouching</td>\n <td>peterporta</td>\n <td>Photo Retouching,Photography,Photography Post-...</td>\n <td>Photography &amp; Video</td>\n <td>Adobe Photoshop</td>\n </tr>\n <tr>\n <th>6</th>\n <td>9</td>\n <td>Reasons to Repeat</td>\n <td>moniquilla</td>\n <td>Advertising,Art Direction,Branding &amp; Identity,...</td>\n <td>Design,Illustration,Marketing &amp; Business,Web &amp;...</td>\n <td>NaN</td>\n </tr>\n <tr>\n <th>7</th>\n <td>10</td>\n <td>Illustrate with Confidence and you will Triumph</td>\n <td>conspiracystudio</td>\n <td>Advertising,Design,Illustration,Vector Illustr...</td>\n <td>Design,Illustration</td>\n <td>Adobe Illustrator,Adobe Photoshop</td>\n </tr>\n <tr>\n <th>8</th>\n <td>11</td>\n <td>Design of Pictograms</td>\n <td>rfaura</td>\n <td>Design,Graphic Design,Icon Design,Photography,...</td>\n <td>3D &amp; Animation,Calligraphy &amp; Typography,Design</td>\n <td>Adobe Freehand,Adobe Illustrator,Adobe InDesign</td>\n </tr>\n <tr>\n <th>9</th>\n <td>12</td>\n <td>Illustrating Time</td>\n <td>stereoplastika</td>\n <td>Advertising,Digital Design,Film Title Design,F...</td>\n <td>3D &amp; Animation,Design,Illustration,Marketing &amp;...</td>\n <td>Adobe Illustrator</td>\n </tr>\n </tbody>\n</table>\n</div>"
},
"metadata": {}
}
]
},
{
"cell_type": "markdown",
"source": "## Get recomendations based on a single column\n\nThis function will take a similarity matrix, the courses data frame, the course id we would like to get recommendations from, and the number of recommendations we need. \n\nThe similarity matrix is a measure of how related are two given courses. We will create this matrix by calculating the cosine similarity between every pair of courses. When doing the recommendations, we will index this matrix and only get the similarities for the course we are interested in. Then, we will sort these values and get the top N. These will be the recommendations.",
"metadata": {}
},
{
"cell_type": "code",
"source": "def get_recommendations_single(similarities, courses, course_id, num_recommendations):\n reverse_index = pd.Series(courses.index, index=courses['id'])\n reverse_id = reverse_index[course_id] \n \n scores = pd.DataFrame(similarities[reverse_id], columns=['score'])\n scores.sort_values(by=[\"score\"], ascending=False, inplace=True)\n \n return pd.concat([scores[:num_recommendations], courses.iloc[scores[:num_recommendations].index]], axis=1)",
"metadata": {
"trusted": true
},
"execution_count": 195,
"outputs": []
},
{
"cell_type": "markdown",
"source": "### Get recomendations based on title\nFor the title, we will first calculate the TF-IDF score for each title and then calculate the cosine similarity between them. After that, we can get our recommendations.",
"metadata": {}
},
{
"cell_type": "code",
"source": "tf_scaler = TfidfVectorizer(analyzer='word', ngram_range=(1, 2), min_df=0, stop_words='english')\ntf_matrix = tf_scaler.fit_transform(courses['title'])\ntf_similarities = linear_kernel(tf_matrix)",
"metadata": {
"trusted": true
},
"execution_count": 6,
"outputs": []
},
{
"cell_type": "code",
"source": "get_recommendations_single(tf_similarities, courses, 404, 10)",
"metadata": {
"trusted": true
},
"execution_count": 7,
"outputs": [
{
"execution_count": 7,
"output_type": "execute_result",
"data": {
"text/plain": " score id title \\\n670 0.345654 835 Photo Composition and Editing for Instagram \n1920 0.323192 2894 Inspiring Visual Content Creation for Instagram \n815 0.275135 1085 Illustrated Filters for Facebook and Instagram... \n2336 0.273887 3510 Content Creation for Instagram: Make a Monthly... \n2755 0.254201 4612 Fabric Dolls: Design and Content Creation \n1700 0.242231 2584 Content Creation: Sustainable Social Media Str... \n1580 0.210046 2408 Content Strategy for Instagram \n820 0.170835 1090 Creative Content Development for Instagram \n1820 0.150274 2759 Curating Instagram Content: Find Your Niche \n\n teacher areas \\\n670 derioi Advertising,Design,Illustration,Instagram,Inst... \n1920 cupofcouple 3D,Advertising,Design,Illustration,Instagram,I... \n815 naranjalidad 3D,3D Design,Design,Digital Illustration,Insta... \n2336 adellinaa Content Marketing,Design,Digital Marketing,Ins... \n2755 lelelerele Arts & Crafts,Design,Illustration,Instagram Ph... \n1700 tiagorhenriques Communication,Content Marketing,Design,Digital... \n1580 julietatello Communication,Design,Digital Marketing,Illustr... \n820 marco_colin Advertising,Communication,Content Marketing,Cr... \n1820 studiosambuckley Content Marketing,Illustration,Instagram,Insta... \n\n categories \\\n670 Photography & Video \n1920 Photography & Video \n815 Illustration \n2336 Marketing & Business,Photography & Video \n2755 Craft,Design,Illustration,Photography & Video \n1700 Marketing & Business \n1580 Marketing & Business \n820 Marketing & Business \n1820 Marketing & Business \n\n softwares \n670 Adobe Lightroom \n1920 VSCO \n815 Adobe Photoshop,Spark AR Studio \n2336 NaN \n2755 NaN \n1700 NaN \n1580 NaN \n820 Procreate \n1820 NaN ",
"text/html": "<div>\n<style scoped>\n .dataframe tbody tr th:only-of-type {\n vertical-align: middle;\n }\n\n .dataframe tbody tr th {\n vertical-align: top;\n }\n\n .dataframe thead th {\n text-align: right;\n }\n</style>\n<table border=\"1\" class=\"dataframe\">\n <thead>\n <tr style=\"text-align: right;\">\n <th></th>\n <th>score</th>\n <th>id</th>\n <th>title</th>\n <th>teacher</th>\n <th>areas</th>\n <th>categories</th>\n <th>softwares</th>\n </tr>\n </thead>\n <tbody>\n <tr>\n <th>670</th>\n <td>0.345654</td>\n <td>835</td>\n <td>Photo Composition and Editing for Instagram</td>\n <td>derioi</td>\n <td>Advertising,Design,Illustration,Instagram,Inst...</td>\n <td>Photography &amp; Video</td>\n <td>Adobe Lightroom</td>\n </tr>\n <tr>\n <th>1920</th>\n <td>0.323192</td>\n <td>2894</td>\n <td>Inspiring Visual Content Creation for Instagram</td>\n <td>cupofcouple</td>\n <td>3D,Advertising,Design,Illustration,Instagram,I...</td>\n <td>Photography &amp; Video</td>\n <td>VSCO</td>\n </tr>\n <tr>\n <th>815</th>\n <td>0.275135</td>\n <td>1085</td>\n <td>Illustrated Filters for Facebook and Instagram...</td>\n <td>naranjalidad</td>\n <td>3D,3D Design,Design,Digital Illustration,Insta...</td>\n <td>Illustration</td>\n <td>Adobe Photoshop,Spark AR Studio</td>\n </tr>\n <tr>\n <th>2336</th>\n <td>0.273887</td>\n <td>3510</td>\n <td>Content Creation for Instagram: Make a Monthly...</td>\n <td>adellinaa</td>\n <td>Content Marketing,Design,Digital Marketing,Ins...</td>\n <td>Marketing &amp; Business,Photography &amp; Video</td>\n <td>NaN</td>\n </tr>\n <tr>\n <th>2755</th>\n <td>0.254201</td>\n <td>4612</td>\n <td>Fabric Dolls: Design and Content Creation</td>\n <td>lelelerele</td>\n <td>Arts &amp; Crafts,Design,Illustration,Instagram Ph...</td>\n <td>Craft,Design,Illustration,Photography &amp; Video</td>\n <td>NaN</td>\n </tr>\n <tr>\n <th>1700</th>\n <td>0.242231</td>\n <td>2584</td>\n <td>Content Creation: Sustainable Social Media Str...</td>\n <td>tiagorhenriques</td>\n <td>Communication,Content Marketing,Design,Digital...</td>\n <td>Marketing &amp; Business</td>\n <td>NaN</td>\n </tr>\n <tr>\n <th>1580</th>\n <td>0.210046</td>\n <td>2408</td>\n <td>Content Strategy for Instagram</td>\n <td>julietatello</td>\n <td>Communication,Design,Digital Marketing,Illustr...</td>\n <td>Marketing &amp; Business</td>\n <td>NaN</td>\n </tr>\n <tr>\n <th>820</th>\n <td>0.170835</td>\n <td>1090</td>\n <td>Creative Content Development for Instagram</td>\n <td>marco_colin</td>\n <td>Advertising,Communication,Content Marketing,Cr...</td>\n <td>Marketing &amp; Business</td>\n <td>Procreate</td>\n </tr>\n <tr>\n <th>1820</th>\n <td>0.150274</td>\n <td>2759</td>\n <td>Curating Instagram Content: Find Your Niche</td>\n <td>studiosambuckley</td>\n <td>Content Marketing,Illustration,Instagram,Insta...</td>\n <td>Marketing &amp; Business</td>\n <td>NaN</td>\n </tr>\n </tbody>\n</table>\n</div>"
},
"metadata": {}
}
]
},
{
"cell_type": "markdown",
"source": "### Get recomendations based on categories\nFor the categories, we will use [one hot encoding](https://machinelearningmastery.com/why-one-hot-encode-data-in-machine-learning/) for creating a vector representation of each course categories that we can then use to compare different courses. We [have to normalize the one hot matrix](https://stackoverflow.com/a/41387245/1563669) to unit lenght before we can compute the similarity matrix so the results make sense. ",
"metadata": {}
},
{
"cell_type": "code",
"source": "one_hot_categories = courses['categories'].astype(str).str.get_dummies(',')\n\noh_categories_scaler = Normalizer()\noh_categories_matrix = oh_categories_scaler.fit_transform(one_hot_categories)\noh_categories_similarities = linear_kernel(oh_categories_matrix)",
"metadata": {
"trusted": true
},
"execution_count": 24,
"outputs": []
},
{
"cell_type": "code",
"source": "get_recommendations_single(oh_categories_similarities, courses, 404, 30)",
"metadata": {
"trusted": true
},
"execution_count": 25,
"outputs": [
{
"execution_count": 25,
"output_type": "execute_result",
"data": {
"text/plain": " score id title \\\n2435 1.000000 3684 Creation and Curation of an Audiovisual Portfolio \n217 1.000000 227 Professional Photography for Instagram \n1825 1.000000 2764 Communication Techniques: Speaking Live and On... \n2375 1.000000 3589 Video Marketing Strategy: Create Ads for Insta... \n365 1.000000 410 Creative Photography for Social Media \n359 1.000000 404 Content Creation and Editing for Instagram Sto... \n2336 1.000000 3510 Content Creation for Instagram: Make a Monthly... \n1840 1.000000 2784 Presenting on Camera: The Art of Communication \n2591 1.000000 4011 Storytelling Strategies for Instagram Reels \n1156 1.000000 1680 Professional Photography: Managing Your Freela... \n1581 1.000000 2409 Mobile Photography and Visual Identity for Ins... \n1582 1.000000 2410 Professional Selfies and Video Selfies for Ins... \n1381 1.000000 2039 Introduction to TikTok for Creatives \n1809 1.000000 2747 YouTube Channel Growth Strategy \n1694 1.000000 2576 Photography for Instagram: Position your Brand \n598 0.816497 712 Branding with Personality \n1390 0.816497 2060 Course 6: Basic Concepts about Spacing \n61 0.816497 64 Introduction to Data Visualization \n1429 0.816497 2122 Course 3: Tracking of Planes and Images \n2523 0.816497 3853 Introduction to Digital Self-Portrait Photography \n2789 0.816497 4886 NFT Art for Beginners: Creation & Marketplaces \n127 0.816497 133 Portrait Illustrated with Photoshop \n150 0.816497 156 Photography for Social Media: Lifestyle Brandi... \n429 0.707107 497 Introduction to Final Cut Pro X \n2235 0.707107 3339 Design Workshops: Boost Creative Collaboration \n2253 0.707107 3369 Architectural Photography: Storytelling for Sp... \n2251 0.707107 3363 Cinematic Portrait Photography: Light and Atmo... \n2549 0.707107 3906 Lifestyle Photography: Express Yourself on Ins... \n2550 0.707107 3907 Social Media for Brands: Strategies to Stand O... \n\n teacher areas \\\n2435 rafajacinto Communication,Creativity,Digital Photography,P... \n217 minabarrio Advertising,Design,Instagram,Instagram Photogr... \n1825 marinaperson Communication,Creativity,Film, Video & TV,Mark... \n2375 canvasco Advertising,Design,Digital Marketing,Facebook ... \n365 annandaniel Art Direction,Fine-Art Photography,Instagram,I... \n359 minabarrio Content Marketing,Digital Marketing,Digital Ph... \n2336 adellinaa Content Marketing,Design,Digital Marketing,Ins... \n1840 person_muller 3D,Advertising,Communication,Design,Film, Vide... \n2591 shinewithnatasha Communication,Content Marketing,Design,Digital... \n1156 dannybbittencourt Creative Consulting,Design,Design Management,F... \n1581 minabarrio Advertising,Instagram,Instagram Photography,Li... \n1582 minabarrio Advertising,Design,Instagram,Instagram Photogr... \n1381 arnulfur Communication,Content Marketing,Digital Market... \n1809 marija_dzemionaite Content Marketing,Digital Marketing,Marketing,... \n1694 beatriztormenta Advertising,Design,Instagram,Instagram Photogr... \n598 pupila Branding & Identity,Creative Consulting,Design... \n1390 wete 3D,Advertising,Art Direction,Branding & Identi... \n61 victorpascual Infographics,Information Architecture,Informat... \n1429 menokillah3d Advertising,Design,Music \n2523 apollinethibault Advertising,Design,Film, Video & TV,Interior P... \n2789 marianpedroza Business,Creative Consulting,Creativity,Design... \n127 oscargimenez Digital Drawing,Digital Illustration,Drawing,P... \n150 holabond Advertising,Art Direction,Branding & Identity,... \n429 giacomo_prestinari Audiovisual Post-production,Design,Film, Video... \n2235 graceascuasiati Business,Communication,Design,Illustration,Inn... \n2253 somosbrutostudio Advertising,Architectural Photography,Design,D... \n2251 tobias_holzweiler Advertising,Design,Film Photography,IT,Portrai... \n2549 oliviathebaut 3D,Advertising,Design,Instagram Photography,Li... \n2550 hanajayklokner Brand Strategy,Illustration,Marketing,Social M... \n\n categories \\\n2435 Marketing & Business,Photography & Video \n217 Marketing & Business,Photography & Video \n1825 Marketing & Business,Photography & Video \n2375 Marketing & Business,Photography & Video \n365 Marketing & Business,Photography & Video \n359 Marketing & Business,Photography & Video \n2336 Marketing & Business,Photography & Video \n1840 Marketing & Business,Photography & Video \n2591 Marketing & Business,Photography & Video \n1156 Marketing & Business,Photography & Video \n1581 Marketing & Business,Photography & Video \n1582 Marketing & Business,Photography & Video \n1381 Marketing & Business,Photography & Video \n1809 Marketing & Business,Photography & Video \n1694 Marketing & Business,Photography & Video \n598 Design,Marketing & Business,Photography & Video \n1390 Design,Marketing & Business,Photography & Video \n61 Design,Marketing & Business,Photography & Video \n1429 Design,Marketing & Business,Photography & Video \n2523 Design,Marketing & Business,Photography & Video \n2789 Design,Marketing & Business,Photography & Video \n127 Illustration,Marketing & Business,Photography ... \n150 Marketing & Business,Photography & Video,Writing \n429 Photography & Video \n2235 Marketing & Business \n2253 Photography & Video \n2251 Photography & Video \n2549 Photography & Video \n2550 Marketing & Business \n\n softwares \n2435 NaN \n217 NaN \n1825 NaN \n2375 NaN \n365 NaN \n359 NaN \n2336 NaN \n1840 NaN \n2591 NaN \n1156 NaN \n1581 NaN \n1582 NaN \n1381 NaN \n1809 NaN \n1694 NaN \n598 Adobe Illustrator,Adobe Photoshop \n1390 NaN \n61 Google Sheets \n1429 NaN \n2523 Adobe Lightroom \n2789 NaN \n127 Adobe Photoshop \n150 Adobe Photoshop \n429 Final Cut Pro \n2235 NaN \n2253 Adobe Bridge,Adobe InDesign,Adobe Photoshop \n2251 Adobe Lightroom \n2549 NaN \n2550 NaN ",
"text/html": "<div>\n<style scoped>\n .dataframe tbody tr th:only-of-type {\n vertical-align: middle;\n }\n\n .dataframe tbody tr th {\n vertical-align: top;\n }\n\n .dataframe thead th {\n text-align: right;\n }\n</style>\n<table border=\"1\" class=\"dataframe\">\n <thead>\n <tr style=\"text-align: right;\">\n <th></th>\n <th>score</th>\n <th>id</th>\n <th>title</th>\n <th>teacher</th>\n <th>areas</th>\n <th>categories</th>\n <th>softwares</th>\n </tr>\n </thead>\n <tbody>\n <tr>\n <th>2435</th>\n <td>1.000000</td>\n <td>3684</td>\n <td>Creation and Curation of an Audiovisual Portfolio</td>\n <td>rafajacinto</td>\n <td>Communication,Creativity,Digital Photography,P...</td>\n <td>Marketing &amp; Business,Photography &amp; Video</td>\n <td>NaN</td>\n </tr>\n <tr>\n <th>217</th>\n <td>1.000000</td>\n <td>227</td>\n <td>Professional Photography for Instagram</td>\n <td>minabarrio</td>\n <td>Advertising,Design,Instagram,Instagram Photogr...</td>\n <td>Marketing &amp; Business,Photography &amp; Video</td>\n <td>NaN</td>\n </tr>\n <tr>\n <th>1825</th>\n <td>1.000000</td>\n <td>2764</td>\n <td>Communication Techniques: Speaking Live and On...</td>\n <td>marinaperson</td>\n <td>Communication,Creativity,Film, Video &amp; TV,Mark...</td>\n <td>Marketing &amp; Business,Photography &amp; Video</td>\n <td>NaN</td>\n </tr>\n <tr>\n <th>2375</th>\n <td>1.000000</td>\n <td>3589</td>\n <td>Video Marketing Strategy: Create Ads for Insta...</td>\n <td>canvasco</td>\n <td>Advertising,Design,Digital Marketing,Facebook ...</td>\n <td>Marketing &amp; Business,Photography &amp; Video</td>\n <td>NaN</td>\n </tr>\n <tr>\n <th>365</th>\n <td>1.000000</td>\n <td>410</td>\n <td>Creative Photography for Social Media</td>\n <td>annandaniel</td>\n <td>Art Direction,Fine-Art Photography,Instagram,I...</td>\n <td>Marketing &amp; Business,Photography &amp; Video</td>\n <td>NaN</td>\n </tr>\n <tr>\n <th>359</th>\n <td>1.000000</td>\n <td>404</td>\n <td>Content Creation and Editing for Instagram Sto...</td>\n <td>minabarrio</td>\n <td>Content Marketing,Digital Marketing,Digital Ph...</td>\n <td>Marketing &amp; Business,Photography &amp; Video</td>\n <td>NaN</td>\n </tr>\n <tr>\n <th>2336</th>\n <td>1.000000</td>\n <td>3510</td>\n <td>Content Creation for Instagram: Make a Monthly...</td>\n <td>adellinaa</td>\n <td>Content Marketing,Design,Digital Marketing,Ins...</td>\n <td>Marketing &amp; Business,Photography &amp; Video</td>\n <td>NaN</td>\n </tr>\n <tr>\n <th>1840</th>\n <td>1.000000</td>\n <td>2784</td>\n <td>Presenting on Camera: The Art of Communication</td>\n <td>person_muller</td>\n <td>3D,Advertising,Communication,Design,Film, Vide...</td>\n <td>Marketing &amp; Business,Photography &amp; Video</td>\n <td>NaN</td>\n </tr>\n <tr>\n <th>2591</th>\n <td>1.000000</td>\n <td>4011</td>\n <td>Storytelling Strategies for Instagram Reels</td>\n <td>shinewithnatasha</td>\n <td>Communication,Content Marketing,Design,Digital...</td>\n <td>Marketing &amp; Business,Photography &amp; Video</td>\n <td>NaN</td>\n </tr>\n <tr>\n <th>1156</th>\n <td>1.000000</td>\n <td>1680</td>\n <td>Professional Photography: Managing Your Freela...</td>\n <td>dannybbittencourt</td>\n <td>Creative Consulting,Design,Design Management,F...</td>\n <td>Marketing &amp; Business,Photography &amp; Video</td>\n <td>NaN</td>\n </tr>\n <tr>\n <th>1581</th>\n <td>1.000000</td>\n <td>2409</td>\n <td>Mobile Photography and Visual Identity for Ins...</td>\n <td>minabarrio</td>\n <td>Advertising,Instagram,Instagram Photography,Li...</td>\n <td>Marketing &amp; Business,Photography &amp; Video</td>\n <td>NaN</td>\n </tr>\n <tr>\n <th>1582</th>\n <td>1.000000</td>\n <td>2410</td>\n <td>Professional Selfies and Video Selfies for Ins...</td>\n <td>minabarrio</td>\n <td>Advertising,Design,Instagram,Instagram Photogr...</td>\n <td>Marketing &amp; Business,Photography &amp; Video</td>\n <td>NaN</td>\n </tr>\n <tr>\n <th>1381</th>\n <td>1.000000</td>\n <td>2039</td>\n <td>Introduction to TikTok for Creatives</td>\n <td>arnulfur</td>\n <td>Communication,Content Marketing,Digital Market...</td>\n <td>Marketing &amp; Business,Photography &amp; Video</td>\n <td>NaN</td>\n </tr>\n <tr>\n <th>1809</th>\n <td>1.000000</td>\n <td>2747</td>\n <td>YouTube Channel Growth Strategy</td>\n <td>marija_dzemionaite</td>\n <td>Content Marketing,Digital Marketing,Marketing,...</td>\n <td>Marketing &amp; Business,Photography &amp; Video</td>\n <td>NaN</td>\n </tr>\n <tr>\n <th>1694</th>\n <td>1.000000</td>\n <td>2576</td>\n <td>Photography for Instagram: Position your Brand</td>\n <td>beatriztormenta</td>\n <td>Advertising,Design,Instagram,Instagram Photogr...</td>\n <td>Marketing &amp; Business,Photography &amp; Video</td>\n <td>NaN</td>\n </tr>\n <tr>\n <th>598</th>\n <td>0.816497</td>\n <td>712</td>\n <td>Branding with Personality</td>\n <td>pupila</td>\n <td>Branding &amp; Identity,Creative Consulting,Design...</td>\n <td>Design,Marketing &amp; Business,Photography &amp; Video</td>\n <td>Adobe Illustrator,Adobe Photoshop</td>\n </tr>\n <tr>\n <th>1390</th>\n <td>0.816497</td>\n <td>2060</td>\n <td>Course 6: Basic Concepts about Spacing</td>\n <td>wete</td>\n <td>3D,Advertising,Art Direction,Branding &amp; Identi...</td>\n <td>Design,Marketing &amp; Business,Photography &amp; Video</td>\n <td>NaN</td>\n </tr>\n <tr>\n <th>61</th>\n <td>0.816497</td>\n <td>64</td>\n <td>Introduction to Data Visualization</td>\n <td>victorpascual</td>\n <td>Infographics,Information Architecture,Informat...</td>\n <td>Design,Marketing &amp; Business,Photography &amp; Video</td>\n <td>Google Sheets</td>\n </tr>\n <tr>\n <th>1429</th>\n <td>0.816497</td>\n <td>2122</td>\n <td>Course 3: Tracking of Planes and Images</td>\n <td>menokillah3d</td>\n <td>Advertising,Design,Music</td>\n <td>Design,Marketing &amp; Business,Photography &amp; Video</td>\n <td>NaN</td>\n </tr>\n <tr>\n <th>2523</th>\n <td>0.816497</td>\n <td>3853</td>\n <td>Introduction to Digital Self-Portrait Photography</td>\n <td>apollinethibault</td>\n <td>Advertising,Design,Film, Video &amp; TV,Interior P...</td>\n <td>Design,Marketing &amp; Business,Photography &amp; Video</td>\n <td>Adobe Lightroom</td>\n </tr>\n <tr>\n <th>2789</th>\n <td>0.816497</td>\n <td>4886</td>\n <td>NFT Art for Beginners: Creation &amp; Marketplaces</td>\n <td>marianpedroza</td>\n <td>Business,Creative Consulting,Creativity,Design...</td>\n <td>Design,Marketing &amp; Business,Photography &amp; Video</td>\n <td>NaN</td>\n </tr>\n <tr>\n <th>127</th>\n <td>0.816497</td>\n <td>133</td>\n <td>Portrait Illustrated with Photoshop</td>\n <td>oscargimenez</td>\n <td>Digital Drawing,Digital Illustration,Drawing,P...</td>\n <td>Illustration,Marketing &amp; Business,Photography ...</td>\n <td>Adobe Photoshop</td>\n </tr>\n <tr>\n <th>150</th>\n <td>0.816497</td>\n <td>156</td>\n <td>Photography for Social Media: Lifestyle Brandi...</td>\n <td>holabond</td>\n <td>Advertising,Art Direction,Branding &amp; Identity,...</td>\n <td>Marketing &amp; Business,Photography &amp; Video,Writing</td>\n <td>Adobe Photoshop</td>\n </tr>\n <tr>\n <th>429</th>\n <td>0.707107</td>\n <td>497</td>\n <td>Introduction to Final Cut Pro X</td>\n <td>giacomo_prestinari</td>\n <td>Audiovisual Post-production,Design,Film, Video...</td>\n <td>Photography &amp; Video</td>\n <td>Final Cut Pro</td>\n </tr>\n <tr>\n <th>2235</th>\n <td>0.707107</td>\n <td>3339</td>\n <td>Design Workshops: Boost Creative Collaboration</td>\n <td>graceascuasiati</td>\n <td>Business,Communication,Design,Illustration,Inn...</td>\n <td>Marketing &amp; Business</td>\n <td>NaN</td>\n </tr>\n <tr>\n <th>2253</th>\n <td>0.707107</td>\n <td>3369</td>\n <td>Architectural Photography: Storytelling for Sp...</td>\n <td>somosbrutostudio</td>\n <td>Advertising,Architectural Photography,Design,D...</td>\n <td>Photography &amp; Video</td>\n <td>Adobe Bridge,Adobe InDesign,Adobe Photoshop</td>\n </tr>\n <tr>\n <th>2251</th>\n <td>0.707107</td>\n <td>3363</td>\n <td>Cinematic Portrait Photography: Light and Atmo...</td>\n <td>tobias_holzweiler</td>\n <td>Advertising,Design,Film Photography,IT,Portrai...</td>\n <td>Photography &amp; Video</td>\n <td>Adobe Lightroom</td>\n </tr>\n <tr>\n <th>2549</th>\n <td>0.707107</td>\n <td>3906</td>\n <td>Lifestyle Photography: Express Yourself on Ins...</td>\n <td>oliviathebaut</td>\n <td>3D,Advertising,Design,Instagram Photography,Li...</td>\n <td>Photography &amp; Video</td>\n <td>NaN</td>\n </tr>\n <tr>\n <th>2550</th>\n <td>0.707107</td>\n <td>3907</td>\n <td>Social Media for Brands: Strategies to Stand O...</td>\n <td>hanajayklokner</td>\n <td>Brand Strategy,Illustration,Marketing,Social M...</td>\n <td>Marketing &amp; Business</td>\n <td>NaN</td>\n </tr>\n </tbody>\n</table>\n</div>"
},
"metadata": {}
}
]
},
{
"cell_type": "markdown",
"source": "### Get recomendations based on teacher\nWe have made the teacher a categorical variable as well. We will follow the same strategy as for the categories. However, given the large number of teachers, this case would be a good candidate to use a more compact representation like the [hashing trick](https://medium.com/value-stream-design/introducing-one-of-the-best-hacks-in-machine-learning-the-hashing-trick-bf6a9c8af18f).",
"metadata": {}
},
{
"cell_type": "code",
"source": "one_hot_teacher = courses['teacher'].astype(str).str.get_dummies()\n\noh_teacher_scaler = Normalizer()\noh_teacher_matrix = oh_teacher_scaler.fit_transform(one_hot_teacher)\noh_teacher_similarities = linear_kernel(oh_teacher_matrix)",
"metadata": {
"trusted": true
},
"execution_count": 10,
"outputs": []
},
{
"cell_type": "code",
"source": "get_recommendations_single(oh_teacher_similarities, courses, 404, 10)",
"metadata": {
"trusted": true
},
"execution_count": 11,
"outputs": [
{
"execution_count": 11,
"output_type": "execute_result",
"data": {
"text/plain": " score id title \\\n1581 1.0 2409 Mobile Photography and Visual Identity for Ins... \n359 1.0 404 Content Creation and Editing for Instagram Sto... \n1582 1.0 2410 Professional Selfies and Video Selfies for Ins... \n0 0.0 1 Illustration of a Pin-Up with Traditional Tech... \n1867 0.0 2820 Botanical Tattoo Design with Procreate \n1859 0.0 2809 Mixed Media Portraiture: Capturing Expressions... \n1860 0.0 2810 Crochet Techniques for Colorful Clothing \n1861 0.0 2813 Creative Writing Lab: Experiment with Words \n1862 0.0 2814 Artistic Floral Watercolor: Connect with Nature \n\n teacher areas \\\n1581 minabarrio Advertising,Instagram,Instagram Photography,Li... \n359 minabarrio Content Marketing,Digital Marketing,Digital Ph... \n1582 minabarrio Advertising,Design,Instagram,Instagram Photogr... \n0 fernando_vicente 3D,Animation,Character Design,Drawing,Film, Vi... \n1867 jentonic 3D,Botanical Illustration,Digital Illustration... \n1859 dsegrove Artistic Drawing,Design,Drawing,Fine Arts,Illu... \n1860 marie_castro Crochet,DIY,Fashion Design,Fiber Arts,Informat... \n1861 karenvilleda Advertising,Creative Writing,Creativity,Fictio... \n1862 inga_buive Botanical Illustration,Design,Illustration,Pai... \n\n categories softwares \n1581 Marketing & Business,Photography & Video NaN \n359 Marketing & Business,Photography & Video NaN \n1582 Marketing & Business,Photography & Video NaN \n0 Illustration,Web & App Design NaN \n1867 Illustration NaN \n1859 Illustration NaN \n1860 Craft,Design,Fashion,Photography & Video,Web &... NaN \n1861 Writing NaN \n1862 Illustration NaN ",
"text/html": "<div>\n<style scoped>\n .dataframe tbody tr th:only-of-type {\n vertical-align: middle;\n }\n\n .dataframe tbody tr th {\n vertical-align: top;\n }\n\n .dataframe thead th {\n text-align: right;\n }\n</style>\n<table border=\"1\" class=\"dataframe\">\n <thead>\n <tr style=\"text-align: right;\">\n <th></th>\n <th>score</th>\n <th>id</th>\n <th>title</th>\n <th>teacher</th>\n <th>areas</th>\n <th>categories</th>\n <th>softwares</th>\n </tr>\n </thead>\n <tbody>\n <tr>\n <th>1581</th>\n <td>1.0</td>\n <td>2409</td>\n <td>Mobile Photography and Visual Identity for Ins...</td>\n <td>minabarrio</td>\n <td>Advertising,Instagram,Instagram Photography,Li...</td>\n <td>Marketing &amp; Business,Photography &amp; Video</td>\n <td>NaN</td>\n </tr>\n <tr>\n <th>359</th>\n <td>1.0</td>\n <td>404</td>\n <td>Content Creation and Editing for Instagram Sto...</td>\n <td>minabarrio</td>\n <td>Content Marketing,Digital Marketing,Digital Ph...</td>\n <td>Marketing &amp; Business,Photography &amp; Video</td>\n <td>NaN</td>\n </tr>\n <tr>\n <th>1582</th>\n <td>1.0</td>\n <td>2410</td>\n <td>Professional Selfies and Video Selfies for Ins...</td>\n <td>minabarrio</td>\n <td>Advertising,Design,Instagram,Instagram Photogr...</td>\n <td>Marketing &amp; Business,Photography &amp; Video</td>\n <td>NaN</td>\n </tr>\n <tr>\n <th>0</th>\n <td>0.0</td>\n <td>1</td>\n <td>Illustration of a Pin-Up with Traditional Tech...</td>\n <td>fernando_vicente</td>\n <td>3D,Animation,Character Design,Drawing,Film, Vi...</td>\n <td>Illustration,Web &amp; App Design</td>\n <td>NaN</td>\n </tr>\n <tr>\n <th>1867</th>\n <td>0.0</td>\n <td>2820</td>\n <td>Botanical Tattoo Design with Procreate</td>\n <td>jentonic</td>\n <td>3D,Botanical Illustration,Digital Illustration...</td>\n <td>Illustration</td>\n <td>NaN</td>\n </tr>\n <tr>\n <th>1859</th>\n <td>0.0</td>\n <td>2809</td>\n <td>Mixed Media Portraiture: Capturing Expressions...</td>\n <td>dsegrove</td>\n <td>Artistic Drawing,Design,Drawing,Fine Arts,Illu...</td>\n <td>Illustration</td>\n <td>NaN</td>\n </tr>\n <tr>\n <th>1860</th>\n <td>0.0</td>\n <td>2810</td>\n <td>Crochet Techniques for Colorful Clothing</td>\n <td>marie_castro</td>\n <td>Crochet,DIY,Fashion Design,Fiber Arts,Informat...</td>\n <td>Craft,Design,Fashion,Photography &amp; Video,Web &amp;...</td>\n <td>NaN</td>\n </tr>\n <tr>\n <th>1861</th>\n <td>0.0</td>\n <td>2813</td>\n <td>Creative Writing Lab: Experiment with Words</td>\n <td>karenvilleda</td>\n <td>Advertising,Creative Writing,Creativity,Fictio...</td>\n <td>Writing</td>\n <td>NaN</td>\n </tr>\n <tr>\n <th>1862</th>\n <td>0.0</td>\n <td>2814</td>\n <td>Artistic Floral Watercolor: Connect with Nature</td>\n <td>inga_buive</td>\n <td>Botanical Illustration,Design,Illustration,Pai...</td>\n <td>Illustration</td>\n <td>NaN</td>\n </tr>\n </tbody>\n</table>\n</div>"
},
"metadata": {}
}
]
},
{
"cell_type": "markdown",
"source": "## Get recomendations based on multiple columns\n\nIn this case, we will compute the weighted sum of multiple similarity matrices and use the result to assign a final score to each candidate course.",
"metadata": {}
},
{
"cell_type": "code",
"source": "def get_recommendations_multiple(similarities, courses, course_id, num_recommendations, weights=None):\n reverse_index = pd.Series(courses.index, index=courses['id'])\n reverse_id = reverse_index[course_id] \n \n if weights is None:\n weights = np.ones(len(similarities))\n \n weighted_similarities = sum(s * w for s, w in zip(similarities, weights))\n \n scores = pd.DataFrame(weighted_similarities[reverse_id], columns=['score'])\n scores.sort_values(by=[\"score\"], ascending=False, inplace=True)\n \n return pd.concat([scores[1:num_recommendations], courses.iloc[scores[1:num_recommendations].index]], axis=1)",
"metadata": {
"trusted": true
},
"execution_count": 12,
"outputs": []
},
{
"cell_type": "code",
"source": "similarities = [tf_similarities, oh_categories_similarities, oh_teacher_similarities]\nget_recommendations_multiple(similarities, courses, 404, 10) ",
"metadata": {
"trusted": true
},
"execution_count": 13,
"outputs": [
{
"execution_count": 13,
"output_type": "execute_result",
"data": {
"text/plain": " score id title \\\n217 2.097958 227 Professional Photography for Instagram \n1581 2.066661 2409 Mobile Photography and Visual Identity for Ins... \n1582 2.050043 2410 Professional Selfies and Video Selfies for Ins... \n2336 1.273887 3510 Content Creation for Instagram: Make a Monthly... \n1694 1.073171 2576 Photography for Instagram: Position your Brand \n2591 1.065960 4011 Storytelling Strategies for Instagram Reels \n2375 1.049998 3589 Video Marketing Strategy: Create Ads for Insta... \n2435 1.042316 3684 Creation and Curation of an Audiovisual Portfolio \n1809 1.000000 2747 YouTube Channel Growth Strategy \n\n teacher areas \\\n217 minabarrio Advertising,Design,Instagram,Instagram Photogr... \n1581 minabarrio Advertising,Instagram,Instagram Photography,Li... \n1582 minabarrio Advertising,Design,Instagram,Instagram Photogr... \n2336 adellinaa Content Marketing,Design,Digital Marketing,Ins... \n1694 beatriztormenta Advertising,Design,Instagram,Instagram Photogr... \n2591 shinewithnatasha Communication,Content Marketing,Design,Digital... \n2375 canvasco Advertising,Design,Digital Marketing,Facebook ... \n2435 rafajacinto Communication,Creativity,Digital Photography,P... \n1809 marija_dzemionaite Content Marketing,Digital Marketing,Marketing,... \n\n categories softwares \n217 Marketing & Business,Photography & Video NaN \n1581 Marketing & Business,Photography & Video NaN \n1582 Marketing & Business,Photography & Video NaN \n2336 Marketing & Business,Photography & Video NaN \n1694 Marketing & Business,Photography & Video NaN \n2591 Marketing & Business,Photography & Video NaN \n2375 Marketing & Business,Photography & Video NaN \n2435 Marketing & Business,Photography & Video NaN \n1809 Marketing & Business,Photography & Video NaN ",
"text/html": "<div>\n<style scoped>\n .dataframe tbody tr th:only-of-type {\n vertical-align: middle;\n }\n\n .dataframe tbody tr th {\n vertical-align: top;\n }\n\n .dataframe thead th {\n text-align: right;\n }\n</style>\n<table border=\"1\" class=\"dataframe\">\n <thead>\n <tr style=\"text-align: right;\">\n <th></th>\n <th>score</th>\n <th>id</th>\n <th>title</th>\n <th>teacher</th>\n <th>areas</th>\n <th>categories</th>\n <th>softwares</th>\n </tr>\n </thead>\n <tbody>\n <tr>\n <th>217</th>\n <td>2.097958</td>\n <td>227</td>\n <td>Professional Photography for Instagram</td>\n <td>minabarrio</td>\n <td>Advertising,Design,Instagram,Instagram Photogr...</td>\n <td>Marketing &amp; Business,Photography &amp; Video</td>\n <td>NaN</td>\n </tr>\n <tr>\n <th>1581</th>\n <td>2.066661</td>\n <td>2409</td>\n <td>Mobile Photography and Visual Identity for Ins...</td>\n <td>minabarrio</td>\n <td>Advertising,Instagram,Instagram Photography,Li...</td>\n <td>Marketing &amp; Business,Photography &amp; Video</td>\n <td>NaN</td>\n </tr>\n <tr>\n <th>1582</th>\n <td>2.050043</td>\n <td>2410</td>\n <td>Professional Selfies and Video Selfies for Ins...</td>\n <td>minabarrio</td>\n <td>Advertising,Design,Instagram,Instagram Photogr...</td>\n <td>Marketing &amp; Business,Photography &amp; Video</td>\n <td>NaN</td>\n </tr>\n <tr>\n <th>2336</th>\n <td>1.273887</td>\n <td>3510</td>\n <td>Content Creation for Instagram: Make a Monthly...</td>\n <td>adellinaa</td>\n <td>Content Marketing,Design,Digital Marketing,Ins...</td>\n <td>Marketing &amp; Business,Photography &amp; Video</td>\n <td>NaN</td>\n </tr>\n <tr>\n <th>1694</th>\n <td>1.073171</td>\n <td>2576</td>\n <td>Photography for Instagram: Position your Brand</td>\n <td>beatriztormenta</td>\n <td>Advertising,Design,Instagram,Instagram Photogr...</td>\n <td>Marketing &amp; Business,Photography &amp; Video</td>\n <td>NaN</td>\n </tr>\n <tr>\n <th>2591</th>\n <td>1.065960</td>\n <td>4011</td>\n <td>Storytelling Strategies for Instagram Reels</td>\n <td>shinewithnatasha</td>\n <td>Communication,Content Marketing,Design,Digital...</td>\n <td>Marketing &amp; Business,Photography &amp; Video</td>\n <td>NaN</td>\n </tr>\n <tr>\n <th>2375</th>\n <td>1.049998</td>\n <td>3589</td>\n <td>Video Marketing Strategy: Create Ads for Insta...</td>\n <td>canvasco</td>\n <td>Advertising,Design,Digital Marketing,Facebook ...</td>\n <td>Marketing &amp; Business,Photography &amp; Video</td>\n <td>NaN</td>\n </tr>\n <tr>\n <th>2435</th>\n <td>1.042316</td>\n <td>3684</td>\n <td>Creation and Curation of an Audiovisual Portfolio</td>\n <td>rafajacinto</td>\n <td>Communication,Creativity,Digital Photography,P...</td>\n <td>Marketing &amp; Business,Photography &amp; Video</td>\n <td>NaN</td>\n </tr>\n <tr>\n <th>1809</th>\n <td>1.000000</td>\n <td>2747</td>\n <td>YouTube Channel Growth Strategy</td>\n <td>marija_dzemionaite</td>\n <td>Content Marketing,Digital Marketing,Marketing,...</td>\n <td>Marketing &amp; Business,Photography &amp; Video</td>\n <td>NaN</td>\n </tr>\n </tbody>\n</table>\n</div>"
},
"metadata": {}
}
]
},
{
"cell_type": "code",
"source": "similarities = [tf_similarities, oh_categories_similarities, oh_teacher_similarities]\nweights = [1, 2, 0.15]\n\nget_recommendations_multiple(similarities, courses, 404, 10, weights) ",
"metadata": {
"trusted": true
},
"execution_count": 14,
"outputs": [
{
"execution_count": 14,
"output_type": "execute_result",
"data": {
"text/plain": " score id title \\\n2336 2.273887 3510 Content Creation for Instagram: Make a Monthly... \n217 2.247958 227 Professional Photography for Instagram \n1581 2.216661 2409 Mobile Photography and Visual Identity for Ins... \n1582 2.200043 2410 Professional Selfies and Video Selfies for Ins... \n1694 2.073171 2576 Photography for Instagram: Position your Brand \n2591 2.065960 4011 Storytelling Strategies for Instagram Reels \n2375 2.049998 3589 Video Marketing Strategy: Create Ads for Insta... \n2435 2.042316 3684 Creation and Curation of an Audiovisual Portfolio \n1809 2.000000 2747 YouTube Channel Growth Strategy \n\n teacher areas \\\n2336 adellinaa Content Marketing,Design,Digital Marketing,Ins... \n217 minabarrio Advertising,Design,Instagram,Instagram Photogr... \n1581 minabarrio Advertising,Instagram,Instagram Photography,Li... \n1582 minabarrio Advertising,Design,Instagram,Instagram Photogr... \n1694 beatriztormenta Advertising,Design,Instagram,Instagram Photogr... \n2591 shinewithnatasha Communication,Content Marketing,Design,Digital... \n2375 canvasco Advertising,Design,Digital Marketing,Facebook ... \n2435 rafajacinto Communication,Creativity,Digital Photography,P... \n1809 marija_dzemionaite Content Marketing,Digital Marketing,Marketing,... \n\n categories softwares \n2336 Marketing & Business,Photography & Video NaN \n217 Marketing & Business,Photography & Video NaN \n1581 Marketing & Business,Photography & Video NaN \n1582 Marketing & Business,Photography & Video NaN \n1694 Marketing & Business,Photography & Video NaN \n2591 Marketing & Business,Photography & Video NaN \n2375 Marketing & Business,Photography & Video NaN \n2435 Marketing & Business,Photography & Video NaN \n1809 Marketing & Business,Photography & Video NaN ",
"text/html": "<div>\n<style scoped>\n .dataframe tbody tr th:only-of-type {\n vertical-align: middle;\n }\n\n .dataframe tbody tr th {\n vertical-align: top;\n }\n\n .dataframe thead th {\n text-align: right;\n }\n</style>\n<table border=\"1\" class=\"dataframe\">\n <thead>\n <tr style=\"text-align: right;\">\n <th></th>\n <th>score</th>\n <th>id</th>\n <th>title</th>\n <th>teacher</th>\n <th>areas</th>\n <th>categories</th>\n <th>softwares</th>\n </tr>\n </thead>\n <tbody>\n <tr>\n <th>2336</th>\n <td>2.273887</td>\n <td>3510</td>\n <td>Content Creation for Instagram: Make a Monthly...</td>\n <td>adellinaa</td>\n <td>Content Marketing,Design,Digital Marketing,Ins...</td>\n <td>Marketing &amp; Business,Photography &amp; Video</td>\n <td>NaN</td>\n </tr>\n <tr>\n <th>217</th>\n <td>2.247958</td>\n <td>227</td>\n <td>Professional Photography for Instagram</td>\n <td>minabarrio</td>\n <td>Advertising,Design,Instagram,Instagram Photogr...</td>\n <td>Marketing &amp; Business,Photography &amp; Video</td>\n <td>NaN</td>\n </tr>\n <tr>\n <th>1581</th>\n <td>2.216661</td>\n <td>2409</td>\n <td>Mobile Photography and Visual Identity for Ins...</td>\n <td>minabarrio</td>\n <td>Advertising,Instagram,Instagram Photography,Li...</td>\n <td>Marketing &amp; Business,Photography &amp; Video</td>\n <td>NaN</td>\n </tr>\n <tr>\n <th>1582</th>\n <td>2.200043</td>\n <td>2410</td>\n <td>Professional Selfies and Video Selfies for Ins...</td>\n <td>minabarrio</td>\n <td>Advertising,Design,Instagram,Instagram Photogr...</td>\n <td>Marketing &amp; Business,Photography &amp; Video</td>\n <td>NaN</td>\n </tr>\n <tr>\n <th>1694</th>\n <td>2.073171</td>\n <td>2576</td>\n <td>Photography for Instagram: Position your Brand</td>\n <td>beatriztormenta</td>\n <td>Advertising,Design,Instagram,Instagram Photogr...</td>\n <td>Marketing &amp; Business,Photography &amp; Video</td>\n <td>NaN</td>\n </tr>\n <tr>\n <th>2591</th>\n <td>2.065960</td>\n <td>4011</td>\n <td>Storytelling Strategies for Instagram Reels</td>\n <td>shinewithnatasha</td>\n <td>Communication,Content Marketing,Design,Digital...</td>\n <td>Marketing &amp; Business,Photography &amp; Video</td>\n <td>NaN</td>\n </tr>\n <tr>\n <th>2375</th>\n <td>2.049998</td>\n <td>3589</td>\n <td>Video Marketing Strategy: Create Ads for Insta...</td>\n <td>canvasco</td>\n <td>Advertising,Design,Digital Marketing,Facebook ...</td>\n <td>Marketing &amp; Business,Photography &amp; Video</td>\n <td>NaN</td>\n </tr>\n <tr>\n <th>2435</th>\n <td>2.042316</td>\n <td>3684</td>\n <td>Creation and Curation of an Audiovisual Portfolio</td>\n <td>rafajacinto</td>\n <td>Communication,Creativity,Digital Photography,P...</td>\n <td>Marketing &amp; Business,Photography &amp; Video</td>\n <td>NaN</td>\n </tr>\n <tr>\n <th>1809</th>\n <td>2.000000</td>\n <td>2747</td>\n <td>YouTube Channel Growth Strategy</td>\n <td>marija_dzemionaite</td>\n <td>Content Marketing,Digital Marketing,Marketing,...</td>\n <td>Marketing &amp; Business,Photography &amp; Video</td>\n <td>NaN</td>\n </tr>\n </tbody>\n</table>\n</div>"
},
"metadata": {}
}
]
},
{
"cell_type": "markdown",
"source": "# Generate the recommendations table",
"metadata": {}
},
{
"cell_type": "code",
"source": "def get_top_recommendations(row, n=5): \n return row.sort_values(ascending=False).drop(row.name).iloc[:n].index.to_list()",
"metadata": {
"trusted": true
},
"execution_count": 197,
"outputs": []
},
{
"cell_type": "code",
"source": "weighted_similarities = sum(s * w for s, w in zip(similarities, weights))\ncourse_similarities = pd.DataFrame(weighted_similarities, index=courses['id'], columns=courses['id'].values)\ncourse_similarities",
"metadata": {
"trusted": true
},
"execution_count": 210,
"outputs": [
{
"execution_count": 210,
"output_type": "execute_result",
"data": {
"text/plain": " 1 2 5 6 7 8 9 10 11 12 ... \\\nid ... \n1 3.150000 0.00 0.00 0.00 0.00 0.0 0.0 0.0 0.0 0.0 ... \n2 0.000000 3.15 0.00 0.00 0.00 0.0 0.0 0.0 0.0 0.0 ... \n5 0.000000 0.00 3.15 0.00 0.00 0.0 0.0 0.0 0.0 0.0 ... \n6 0.000000 0.00 0.00 3.15 0.00 0.0 0.0 0.0 0.0 0.0 ... \n7 0.000000 0.00 0.00 0.00 3.15 0.0 0.0 0.0 0.0 0.0 ... \n... ... ... ... ... ... ... ... ... ... ... ... \n4877 0.039713 0.00 0.00 0.00 0.00 0.0 0.0 0.0 0.0 0.0 ... \n4886 0.000000 0.00 0.00 0.00 0.00 0.0 0.0 0.0 0.0 0.0 ... \n4948 0.033012 0.00 0.00 0.00 0.00 0.0 0.0 0.0 0.0 0.0 ... \n5041 0.000000 0.00 0.00 0.00 0.00 0.0 0.0 0.0 0.0 0.0 ... \n5047 0.000000 0.00 0.00 0.00 0.00 0.0 0.0 0.0 0.0 0.0 ... \n\n 4816 4819 4824 4855 4857 4877 4886 \\\nid \n1 0.035731 0.000000 0.000000 0.000000 0.0 0.039713 0.000000 \n2 0.000000 0.000000 0.000000 0.000000 0.0 0.000000 0.000000 \n5 0.000000 0.000000 0.000000 0.000000 0.0 0.000000 0.000000 \n6 0.000000 0.000000 0.000000 0.000000 0.0 0.000000 0.000000 \n7 0.000000 0.000000 0.000000 0.000000 0.0 0.000000 0.000000 \n... ... ... ... ... ... ... ... \n4877 0.036930 0.000000 0.000000 0.000000 0.0 3.150000 0.000000 \n4886 0.000000 0.039296 0.033127 0.038978 0.0 0.000000 3.150000 \n4948 0.030699 0.000000 0.000000 0.040060 0.0 0.034120 0.037724 \n5041 0.000000 0.000000 0.000000 0.000000 0.0 0.000000 0.000000 \n5047 0.000000 0.000000 0.000000 0.000000 0.0 0.000000 0.000000 \n\n 4948 5041 5047 \nid \n1 0.033012 0.000000 0.000000 \n2 0.000000 0.000000 0.000000 \n5 0.000000 0.000000 0.000000 \n6 0.000000 0.000000 0.000000 \n7 0.000000 0.000000 0.000000 \n... ... ... ... \n4877 0.034120 0.000000 0.000000 \n4886 0.037724 0.000000 0.000000 \n4948 3.150000 0.000000 0.000000 \n5041 0.000000 3.150000 0.335581 \n5047 0.000000 0.335581 3.150000 \n\n[2793 rows x 2793 columns]",
"text/html": "<div>\n<style scoped>\n .dataframe tbody tr th:only-of-type {\n vertical-align: middle;\n }\n\n .dataframe tbody tr th {\n vertical-align: top;\n }\n\n .dataframe thead th {\n text-align: right;\n }\n</style>\n<table border=\"1\" class=\"dataframe\">\n <thead>\n <tr style=\"text-align: right;\">\n <th></th>\n <th>1</th>\n <th>2</th>\n <th>5</th>\n <th>6</th>\n <th>7</th>\n <th>8</th>\n <th>9</th>\n <th>10</th>\n <th>11</th>\n <th>12</th>\n <th>...</th>\n <th>4816</th>\n <th>4819</th>\n <th>4824</th>\n <th>4855</th>\n <th>4857</th>\n <th>4877</th>\n <th>4886</th>\n <th>4948</th>\n <th>5041</th>\n <th>5047</th>\n </tr>\n <tr>\n <th>id</th>\n <th></th>\n <th></th>\n <th></th>\n <th></th>\n <th></th>\n <th></th>\n <th></th>\n <th></th>\n <th></th>\n <th></th>\n <th></th>\n <th></th>\n <th></th>\n <th></th>\n <th></th>\n <th></th>\n <th></th>\n <th></th>\n <th></th>\n <th></th>\n <th></th>\n </tr>\n </thead>\n <tbody>\n <tr>\n <th>1</th>\n <td>3.150000</td>\n <td>0.00</td>\n <td>0.00</td>\n <td>0.00</td>\n <td>0.00</td>\n <td>0.0</td>\n <td>0.0</td>\n <td>0.0</td>\n <td>0.0</td>\n <td>0.0</td>\n <td>...</td>\n <td>0.035731</td>\n <td>0.000000</td>\n <td>0.000000</td>\n <td>0.000000</td>\n <td>0.0</td>\n <td>0.039713</td>\n <td>0.000000</td>\n <td>0.033012</td>\n <td>0.000000</td>\n <td>0.000000</td>\n </tr>\n <tr>\n <th>2</th>\n <td>0.000000</td>\n <td>3.15</td>\n <td>0.00</td>\n <td>0.00</td>\n <td>0.00</td>\n <td>0.0</td>\n <td>0.0</td>\n <td>0.0</td>\n <td>0.0</td>\n <td>0.0</td>\n <td>...</td>\n <td>0.000000</td>\n <td>0.000000</td>\n <td>0.000000</td>\n <td>0.000000</td>\n <td>0.0</td>\n <td>0.000000</td>\n <td>0.000000</td>\n <td>0.000000</td>\n <td>0.000000</td>\n <td>0.000000</td>\n </tr>\n <tr>\n <th>5</th>\n <td>0.000000</td>\n <td>0.00</td>\n <td>3.15</td>\n <td>0.00</td>\n <td>0.00</td>\n <td>0.0</td>\n <td>0.0</td>\n <td>0.0</td>\n <td>0.0</td>\n <td>0.0</td>\n <td>...</td>\n <td>0.000000</td>\n <td>0.000000</td>\n <td>0.000000</td>\n <td>0.000000</td>\n <td>0.0</td>\n <td>0.000000</td>\n <td>0.000000</td>\n <td>0.000000</td>\n <td>0.000000</td>\n <td>0.000000</td>\n </tr>\n <tr>\n <th>6</th>\n <td>0.000000</td>\n <td>0.00</td>\n <td>0.00</td>\n <td>3.15</td>\n <td>0.00</td>\n <td>0.0</td>\n <td>0.0</td>\n <td>0.0</td>\n <td>0.0</td>\n <td>0.0</td>\n <td>...</td>\n <td>0.000000</td>\n <td>0.000000</td>\n <td>0.000000</td>\n <td>0.000000</td>\n <td>0.0</td>\n <td>0.000000</td>\n <td>0.000000</td>\n <td>0.000000</td>\n <td>0.000000</td>\n <td>0.000000</td>\n </tr>\n <tr>\n <th>7</th>\n <td>0.000000</td>\n <td>0.00</td>\n <td>0.00</td>\n <td>0.00</td>\n <td>3.15</td>\n <td>0.0</td>\n <td>0.0</td>\n <td>0.0</td>\n <td>0.0</td>\n <td>0.0</td>\n <td>...</td>\n <td>0.000000</td>\n <td>0.000000</td>\n <td>0.000000</td>\n <td>0.000000</td>\n <td>0.0</td>\n <td>0.000000</td>\n <td>0.000000</td>\n <td>0.000000</td>\n <td>0.000000</td>\n <td>0.000000</td>\n </tr>\n <tr>\n <th>...</th>\n <td>...</td>\n <td>...</td>\n <td>...</td>\n <td>...</td>\n <td>...</td>\n <td>...</td>\n <td>...</td>\n <td>...</td>\n <td>...</td>\n <td>...</td>\n <td>...</td>\n <td>...</td>\n <td>...</td>\n <td>...</td>\n <td>...</td>\n <td>...</td>\n <td>...</td>\n <td>...</td>\n <td>...</td>\n <td>...</td>\n <td>...</td>\n </tr>\n <tr>\n <th>4877</th>\n <td>0.039713</td>\n <td>0.00</td>\n <td>0.00</td>\n <td>0.00</td>\n <td>0.00</td>\n <td>0.0</td>\n <td>0.0</td>\n <td>0.0</td>\n <td>0.0</td>\n <td>0.0</td>\n <td>...</td>\n <td>0.036930</td>\n <td>0.000000</td>\n <td>0.000000</td>\n <td>0.000000</td>\n <td>0.0</td>\n <td>3.150000</td>\n <td>0.000000</td>\n <td>0.034120</td>\n <td>0.000000</td>\n <td>0.000000</td>\n </tr>\n <tr>\n <th>4886</th>\n <td>0.000000</td>\n <td>0.00</td>\n <td>0.00</td>\n <td>0.00</td>\n <td>0.00</td>\n <td>0.0</td>\n <td>0.0</td>\n <td>0.0</td>\n <td>0.0</td>\n <td>0.0</td>\n <td>...</td>\n <td>0.000000</td>\n <td>0.039296</td>\n <td>0.033127</td>\n <td>0.038978</td>\n <td>0.0</td>\n <td>0.000000</td>\n <td>3.150000</td>\n <td>0.037724</td>\n <td>0.000000</td>\n <td>0.000000</td>\n </tr>\n <tr>\n <th>4948</th>\n <td>0.033012</td>\n <td>0.00</td>\n <td>0.00</td>\n <td>0.00</td>\n <td>0.00</td>\n <td>0.0</td>\n <td>0.0</td>\n <td>0.0</td>\n <td>0.0</td>\n <td>0.0</td>\n <td>...</td>\n <td>0.030699</td>\n <td>0.000000</td>\n <td>0.000000</td>\n <td>0.040060</td>\n <td>0.0</td>\n <td>0.034120</td>\n <td>0.037724</td>\n <td>3.150000</td>\n <td>0.000000</td>\n <td>0.000000</td>\n </tr>\n <tr>\n <th>5041</th>\n <td>0.000000</td>\n <td>0.00</td>\n <td>0.00</td>\n <td>0.00</td>\n <td>0.00</td>\n <td>0.0</td>\n <td>0.0</td>\n <td>0.0</td>\n <td>0.0</td>\n <td>0.0</td>\n <td>...</td>\n <td>0.000000</td>\n <td>0.000000</td>\n <td>0.000000</td>\n <td>0.000000</td>\n <td>0.0</td>\n <td>0.000000</td>\n <td>0.000000</td>\n <td>0.000000</td>\n <td>3.150000</td>\n <td>0.335581</td>\n </tr>\n <tr>\n <th>5047</th>\n <td>0.000000</td>\n <td>0.00</td>\n <td>0.00</td>\n <td>0.00</td>\n <td>0.00</td>\n <td>0.0</td>\n <td>0.0</td>\n <td>0.0</td>\n <td>0.0</td>\n <td>0.0</td>\n <td>...</td>\n <td>0.000000</td>\n <td>0.000000</td>\n <td>0.000000</td>\n <td>0.000000</td>\n <td>0.0</td>\n <td>0.000000</td>\n <td>0.000000</td>\n <td>0.000000</td>\n <td>0.335581</td>\n <td>3.150000</td>\n </tr>\n </tbody>\n</table>\n<p>2793 rows × 2793 columns</p>\n</div>"
},
"metadata": {}
}
]
},
{
"cell_type": "code",
"source": "recommendations_series = course_similarities.apply(get_top_recommendations, axis=1, n=5)\n\nrecommendations_table = pd.DataFrame(recommendations_series, columns=['recommended_course_ids'])\nrecommendations_table = recommendations_table.reset_index().rename(columns = {'id': 'course_id'})\n\nrecommendations_table",
"metadata": {
"trusted": true
},
"execution_count": 220,
"outputs": [
{
"execution_count": 220,
"output_type": "execute_result",
"data": {
"text/plain": " course_id recommended_course_ids\n0 1 [1270, 2833, 28, 1302, 74]\n1 2 [3188, 3296, 341, 4362, 2664]\n2 5 [1632, 1270, 2500, 3836, 1968]\n3 6 [48, 1685, 44, 52, 388]\n4 7 [595, 434, 384, 214, 2621]\n... ... ...\n2788 4877 [154, 482, 1694, 4104, 742]\n2789 4886 [2060, 3853, 64, 712, 2122]\n2790 4948 [1739, 4234, 3065, 1561, 1703]\n2791 5041 [865, 5047, 4151, 2962, 3372]\n2792 5047 [3404, 171, 1837, 98, 4382]\n\n[2793 rows x 2 columns]",
"text/html": "<div>\n<style scoped>\n .dataframe tbody tr th:only-of-type {\n vertical-align: middle;\n }\n\n .dataframe tbody tr th {\n vertical-align: top;\n }\n\n .dataframe thead th {\n text-align: right;\n }\n</style>\n<table border=\"1\" class=\"dataframe\">\n <thead>\n <tr style=\"text-align: right;\">\n <th></th>\n <th>course_id</th>\n <th>recommended_course_ids</th>\n </tr>\n </thead>\n <tbody>\n <tr>\n <th>0</th>\n <td>1</td>\n <td>[1270, 2833, 28, 1302, 74]</td>\n </tr>\n <tr>\n <th>1</th>\n <td>2</td>\n <td>[3188, 3296, 341, 4362, 2664]</td>\n </tr>\n <tr>\n <th>2</th>\n <td>5</td>\n <td>[1632, 1270, 2500, 3836, 1968]</td>\n </tr>\n <tr>\n <th>3</th>\n <td>6</td>\n <td>[48, 1685, 44, 52, 388]</td>\n </tr>\n <tr>\n <th>4</th>\n <td>7</td>\n <td>[595, 434, 384, 214, 2621]</td>\n </tr>\n <tr>\n <th>...</th>\n <td>...</td>\n <td>...</td>\n </tr>\n <tr>\n <th>2788</th>\n <td>4877</td>\n <td>[154, 482, 1694, 4104, 742]</td>\n </tr>\n <tr>\n <th>2789</th>\n <td>4886</td>\n <td>[2060, 3853, 64, 712, 2122]</td>\n </tr>\n <tr>\n <th>2790</th>\n <td>4948</td>\n <td>[1739, 4234, 3065, 1561, 1703]</td>\n </tr>\n <tr>\n <th>2791</th>\n <td>5041</td>\n <td>[865, 5047, 4151, 2962, 3372]</td>\n </tr>\n <tr>\n <th>2792</th>\n <td>5047</td>\n <td>[3404, 171, 1837, 98, 4382]</td>\n </tr>\n </tbody>\n</table>\n<p>2793 rows × 2 columns</p>\n</div>"
},
"metadata": {}
}
]
},
{
"cell_type": "code",
"source": "print(recommendations_table.iloc[:5].to_csv(index=False))",
"metadata": {
"trusted": true
},
"execution_count": 225,
"outputs": [
{
"name": "stdout",
"text": "course_id,recommended_course_ids\n1,\"[1270, 2833, 28, 1302, 74]\"\n2,\"[3188, 3296, 341, 4362, 2664]\"\n5,\"[1632, 1270, 2500, 3836, 1968]\"\n6,\"[48, 1685, 44, 52, 388]\"\n7,\"[595, 434, 384, 214, 2621]\"\n\n",
"output_type": "stream"
}
]
},
{
"cell_type": "markdown",
"source": "## References\n+ https://developers.google.com/machine-learning/recommendation/content-based/basics\n+ https://www.kdnuggets.com/2020/07/building-content-based-book-recommendation-engine.html\n+ https://www.datacamp.com/tutorial/recommender-systems-python\n+ https://biarca.com/2018/09/cosine-similarity-and-handling-categorical-variables/\n+ https://towardsdatascience.com/smarter-ways-to-encode-categorical-data-for-machine-learning-part-1-of-3-6dca2f71b159\n+ https://stats.stackexchange.com/questions/233262/ohe-vs-feature-hashing\n+ https://stackoverflow.com/questions/41387000/cosine-similarity-of-word2vec-more-than-1\n+ https://en.wikipedia.org/wiki/Cosine_similarity",
"metadata": {}
}
]
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment