├── .gitignore ├── 01.scraping.py ├── 02.preprocessing.py ├── 03.modeling.py ├── 04.Visualization.ipynb ├── README.md ├── data ├── cosmetic.csv ├── cosmetic_TSNE.csv └── cosmetic_p.csv └── image ├── image.png └── map.gif /.gitignore: -------------------------------------------------------------------------------- 1 | .ipynb_checkpoints 2 | backup/ 3 | -------------------------------------------------------------------------------- /01.scraping.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | Created on Sat Nov 24 19:56:54 2018 4 | 5 | @author: jjone 6 | """ 7 | 8 | # This is the part 1 of cosmetic recommendation: scraping cosmetic data from sephora 9 | # You can also daownload the csv file from same repository: cosmetic.csv 10 | 11 | import time 12 | 13 | from selenium import webdriver 14 | from selenium.webdriver.common.keys import Keys 15 | from selenium.webdriver.common.by import By 16 | from selenium.common.exceptions import NoSuchElementException 17 | 18 | 19 | chrome_path = "C:\\Users\jjone\Downloads\chromedriver" 20 | 21 | def scrollDown(driver, n_scroll): 22 | body = driver.find_element_by_tag_name("body") 23 | while n_scroll >= 0: 24 | body.send_keys(Keys.PAGE_DOWN) 25 | n_scroll -= 1 26 | return driver 27 | 28 | 29 | driver = webdriver.Chrome(executable_path = chrome_path) 30 | 31 | url = 'https://www.sephora.com' 32 | driver.get(url) 33 | 34 | xpath = '/html/body/div[5]/div/div/div[1]/div/div/button' 35 | btn = driver.find_element_by_xpath(xpath) 36 | btn.click() 37 | xpath2 = '/html/body/div[3]/div/div/div[1]/div/div/div[2]/form/div[3]/div/div[1]/button' 38 | btn = driver.find_element_by_xpath(xpath2) 39 | btn.click() 40 | 41 | # initiate empty dataframe 42 | df = pd.DataFrame(columns=['Label', 'URL']) 43 | print(df) 44 | 45 | # step 1 46 | tickers = ['moisturizing-cream-oils-mists', 'cleanser', 'facial-treatments', 'face-mask', 47 | 'eye-treatment-dark-circle-treatment', 'sunscreen-sun-protection'] 48 | 49 | for ticker in tickers: 50 | url = 'https://www.sephora.com/shop/' + ticker + '?pageSize=300' 51 | driver.get(url) 52 | 53 | xpath = '/html/body/div[5]/div/div/div[1]/div/div/button' 54 | btn = driver.find_element_by_xpath(xpath) 55 | btn.click() 56 | time.sleep(20) 57 | 58 | browser = scrollDown(driver, 10) 59 | time.sleep(10) 60 | 61 | browser = scrollDown(driver, 10) 62 | time.sleep(10) 63 | 64 | browser = scrollDown(driver, 10) 65 | time.sleep(10) 66 | 67 | browser = scrollDown(driver, 10) 68 | 69 | element = driver.find_elements_by_class_name('css-ix8km1') 70 | 71 | subpageURL = [] 72 | for a in element: 73 | subURL = a.get_attribute('href') 74 | subpageURL.append(subURL) 75 | 76 | # transform into a data frame 77 | dic = {'Label': ticker, 'URL': subpageURL} 78 | df = df.append(pd.DataFrame(dic), ignore_index = True) 79 | 80 | # add columns 81 | df2 = pd.DataFrame(columns=['brand', 'name', 'price', 'rank', 'skin_type', 'ingredients']) 82 | df = pd.concat([df, df2], axis = 1) 83 | 84 | # step 2 85 | for i in range(len(df)+1): 86 | url = df.URL[i] 87 | driver.get(url) 88 | time.sleep(5) 89 | 90 | xpath = '/html/body/div[5]/div/div/div[1]/div/div/button' 91 | btn = driver.find_element_by_xpath(xpath) 92 | btn.click() 93 | 94 | # brand, name, price 95 | df.brand[i] = driver.find_element_by_class_name('css-avdj50').text 96 | df.name[i] = driver.find_element_by_class_name('css-r4ddnb ').text 97 | df.price[i] = driver.find_element_by_class_name('css-n8yjg7 ').text 98 | 99 | browser = scrollDown(driver, 1) 100 | time.sleep(5) 101 | browser = scrollDown(driver, 1) 102 | time.sleep(5) 103 | 104 | # skin_type 105 | detail = driver.find_element_by_class_name('css-192qj50').text 106 | pattern = r"✔ \w+\n" 107 | df.skin_type[i] = re.findall(pattern, detail) 108 | 109 | # ingredients 110 | xpath = '//*[@id="tab2"]' 111 | btn = driver.find_element_by_xpath(xpath) 112 | btn.click() 113 | 114 | try: 115 | df.ingredients[i] = driver.find_element_by_xpath('//*[@id="tabpanel2"]/div').text 116 | except NoSuchElementException: 117 | df.ingredients[i] = 'No Info' 118 | 119 | # rank 120 | try: 121 | rank = driver.find_element_by_class_name('css-ffj77u').text 122 | rank = re.match('\d.\d', rank).group() 123 | df['rank'][i] = str(rank) 124 | 125 | except NoSuchElementException: 126 | df['rank'][i] = 0 127 | 128 | print(i) # just for verbose 129 | 130 | 131 | df.to_csv('data/cosmetic.csv', encoding = 'utf-8-sig', index = False) 132 | -------------------------------------------------------------------------------- /02.preprocessing.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | Created on Sat Nov 24 19:56:54 2018 4 | 5 | @author: jjone 6 | """ 7 | 8 | # This is the part 2 of cosmetic recommendation: analyzing cosmetic items similarities based on their ingredients 9 | # You can also daownload the csv file from same repository: cosmetic.csv 10 | 11 | 12 | import pandas as pd 13 | import numpy as np 14 | import re 15 | import matplotlib.pyplot as plt 16 | 17 | # step 1. cleaning data 18 | cosm = pd.read_csv('data/cosmetic.csv') 19 | cosm.info() 20 | 21 | cosm = cosm.loc[pd.notnull(cosm['Ingredients'])] 22 | cosm.info() 23 | 24 | # label 25 | cosm.Label[cosm['Label'] == 'moisturizing-cream-oils-mists'] = str('Moisturizer') 26 | cosm.Label[cosm['Label'] == 'cleanser'] = str('Cleanser') 27 | cosm.Label[cosm['Label'] == 'facial-treatments'] = str('Treatment') 28 | cosm.Label[cosm['Label'] == 'face-mask'] = str('Face Mask') 29 | cosm.Label[cosm['Label'] == 'eye-treatment-dark-circle-treatment'] = str('Eye cream') 30 | cosm.Label[cosm['Label'] == 'sunscreen-sun-protection'] = str('Sun protect') 31 | 32 | # name -> duplicated item 33 | df_2 = cosm['name'].drop_duplicates() 34 | cosm = cosm.loc[df_2.index, :].reset_index() 35 | 36 | # URL 37 | cosm.drop(['URL', 'index'], axis = 1, inplace = True) 38 | 39 | # price 40 | pattern = re.compile(r"(\d+).\d+") 41 | for i in range(len(cosm)): 42 | cosm['price'][i] = re.findall(pattern, cosm['price'][i])[0] 43 | 44 | cosm['price'] = pd.to_numeric(cosm['price']) 45 | 46 | # rank 47 | cosm['rank'].fillna(0, inplace = True) 48 | cosm.info() 49 | 50 | # skin_type 51 | pattern = re.compile(r"([a-zA-Z]+)\\n") 52 | for i in range(len(cosm)): 53 | cosm['skin_type'][i] = re.findall(pattern, cosm['skin_type'][i]) 54 | 55 | ## list column dummies 56 | df_2 = cosm['skin_type'].str.join('|').str.get_dummies() 57 | cosm_2 = cosm.join(df_2).drop('skin_type', axis = 1) 58 | 59 | ## tokenize ingredients 60 | a = [t.split('\r\n\r\n') for t in cosm['Ingredients']] 61 | pattern = ['\r\n', '-\w+: ', 'Please', 'No Info', 'This product', 'Visit'] 62 | 63 | for i in range(len(cosm)): 64 | Num = len(a[i]) 65 | for j in range(Num): 66 | if all(x not in a[i][j] for x in pattern): 67 | cosm_2['Ingredients'][i] = a[i][j] 68 | 69 | # save the file 70 | df.to_csv('data/cosmetic_p.csv', encoding = 'utf-8-sig', index = False) 71 | -------------------------------------------------------------------------------- /03.modeling.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | Created on Sat Nov 24 19:56:54 2018 4 | 5 | @author: jjone 6 | """ 7 | 8 | # This is the part 2 of cosmetic recommendation: analyzing cosmetic items similarities based on their ingredients 9 | # You can also daownload the csv file from same repository: cosmetic.csv 10 | 11 | 12 | import pandas as pd 13 | import numpy as np 14 | from sklearn.manifold import TSNE 15 | 16 | 17 | # Load the data 18 | cosm_2 = pd.read_csv('data/cosmetic_p.csv') 19 | 20 | # All possible combinations for the option choices 21 | option_1 = cosm_2.Label.unique().tolist() 22 | option_2 = cosm_2.columns[6:].tolist() 23 | 24 | 25 | ## defining a function embedding ingredients and decomposition at once 26 | def my_recommender(op_1, op_2): 27 | df = cosm_2[cosm_2['Label'] == op_1][cosm_2[op_2] == 1] 28 | df = df.reset_index() 29 | 30 | # embedding each ingredients 31 | ingredient_idx = {} 32 | corpus = [] 33 | idx = 0 34 | 35 | for i in range(len(df)): 36 | ingreds = df['ingredients'][i] 37 | ingreds = ingreds.lower() 38 | tokens = ingreds.split(', ') 39 | corpus.append(tokens) 40 | for ingredient in tokens: 41 | if ingredient not in ingredient_idx: 42 | ingredient_idx[ingredient] = idx 43 | idx += 1 44 | 45 | # Get the number of items and tokens 46 | M = len(df) # The number of the items 47 | N = len(ingredient_idx) # The number of the ingredients 48 | 49 | # Initialize a matrix of zeros 50 | A = np.zeros(shape = (M, N)) 51 | 52 | # Define the oh_encoder function 53 | def oh_encoder(tokens): 54 | x = np.zeros(N) 55 | for t in tokens: 56 | # Get the index for each ingredient 57 | idx = ingredient_idx[t] 58 | # Put 1 at the corresponding indices 59 | x[idx] = 1 60 | return x 61 | 62 | # Make a document-term matrix 63 | i = 0 64 | for tokens in corpus: 65 | A[i, :] = oh_encoder(tokens) 66 | i += 1 67 | 68 | # Dimension reduction with t-SNE 69 | model = TSNE(n_components = 2, learning_rate = 200) 70 | tsne_features = model.fit_transform(A) 71 | 72 | # Make X, Y columns 73 | df['X'] = tsne_features[:, 0] 74 | df['Y'] = tsne_features[:, 1] 75 | 76 | return df 77 | 78 | 79 | # Create the dataframe for all combinations 80 | df_all = pd.DataFrame() 81 | for op_1 in option_1: 82 | for op_2 in option_2: 83 | temp = my_recommender(op_1, op_2) 84 | temp['Label'] = op_1 + '_' + op_2 85 | df_all = pd.concat([df_all, temp]) 86 | 87 | # Save the file 88 | df_all.to_csv('data/cosmetic_TSNE.csv', encoding = 'utf-8-sig', index = False) 89 | -------------------------------------------------------------------------------- /04.Visualization.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "markdown", 5 | "metadata": { 6 | "colab_type": "text", 7 | "id": "view-in-github" 8 | }, 9 | "source": [ 10 | "\"Open" 11 | ] 12 | }, 13 | { 14 | "cell_type": "markdown", 15 | "metadata": { 16 | "colab_type": "text", 17 | "id": "iRTC5MBSSsoe" 18 | }, 19 | "source": [ 20 | "# Cosmetic Recommendation based on Chemical Composition" 21 | ] 22 | }, 23 | { 24 | "cell_type": "markdown", 25 | "metadata": { 26 | "colab_type": "text", 27 | "id": "pIFjdXCmSsoh" 28 | }, 29 | "source": [ 30 | "This is the project for mapping cosmetic items based on similarities of chemical composition and giving content-based recommendation. The dataset was prepared in advance and the details [here](https://towardsdatascience.com/for-your-skin-beauty-mapping-cosmetic-items-with-bokeh-af7523ca68e5)." 31 | ] 32 | }, 33 | { 34 | "cell_type": "markdown", 35 | "metadata": { 36 | "colab_type": "text", 37 | "id": "aFbxCHdYSsoi" 38 | }, 39 | "source": [ 40 | "## 1. Importing the necessary libraries and the dataset" 41 | ] 42 | }, 43 | { 44 | "cell_type": "code", 45 | "execution_count": 2, 46 | "metadata": { 47 | "colab": {}, 48 | "colab_type": "code", 49 | "id": "83dLb1MKSsok" 50 | }, 51 | "outputs": [], 52 | "source": [ 53 | "# import libraries\n", 54 | "import pandas as pd\n", 55 | "import numpy as np\n", 56 | "from sklearn.metrics.pairwise import cosine_similarity\n", 57 | "\n", 58 | "from bokeh.io import show, curdoc, output_notebook, push_notebook\n", 59 | "from bokeh.plotting import figure\n", 60 | "from bokeh.models import ColumnDataSource, HoverTool, Select, Paragraph, TextInput\n", 61 | "from bokeh.layouts import widgetbox, column, row\n", 62 | "from ipywidgets import interact " 63 | ] 64 | }, 65 | { 66 | "cell_type": "code", 67 | "execution_count": 18, 68 | "metadata": { 69 | "colab": { 70 | "base_uri": "https://localhost:8080/", 71 | "height": 397 72 | }, 73 | "colab_type": "code", 74 | "id": "crZ3yCWC1PXm", 75 | "outputId": "6c7796b1-0436-4a50-ba2a-62e8509053f0" 76 | }, 77 | "outputs": [ 78 | { 79 | "data": { 80 | "text/html": [ 81 | "
\n", 82 | "\n", 95 | "\n", 96 | " \n", 97 | " \n", 98 | " \n", 99 | " \n", 100 | " \n", 101 | " \n", 102 | " \n", 103 | " \n", 104 | " \n", 105 | " \n", 106 | " \n", 107 | " \n", 108 | " \n", 109 | " \n", 110 | " \n", 111 | " \n", 112 | " \n", 113 | " \n", 114 | " \n", 115 | " \n", 116 | " \n", 117 | " \n", 118 | " \n", 119 | " \n", 120 | " \n", 121 | " \n", 122 | " \n", 123 | " \n", 124 | " \n", 125 | " \n", 126 | " \n", 127 | " \n", 128 | " \n", 129 | " \n", 130 | " \n", 131 | " \n", 132 | " \n", 133 | " \n", 134 | " \n", 135 | " \n", 136 | " \n", 137 | " \n", 138 | " \n", 139 | " \n", 140 | " \n", 141 | " \n", 142 | " \n", 143 | " \n", 144 | " \n", 145 | " \n", 146 | " \n", 147 | " \n", 148 | " \n", 149 | " \n", 150 | " \n", 151 | " \n", 152 | " \n", 153 | " \n", 154 | " \n", 155 | " \n", 156 | " \n", 157 | " \n", 158 | " \n", 159 | " \n", 160 | " \n", 161 | " \n", 162 | " \n", 163 | " \n", 164 | " \n", 165 | " \n", 166 | " \n", 167 | " \n", 168 | " \n", 169 | " \n", 170 | " \n", 171 | " \n", 172 | " \n", 173 | " \n", 174 | " \n", 175 | " \n", 176 | " \n", 177 | " \n", 178 | " \n", 179 | " \n", 180 | " \n", 181 | " \n", 182 | " \n", 183 | " \n", 184 | " \n", 185 | " \n", 186 | " \n", 187 | " \n", 188 | " \n", 189 | " \n", 190 | " \n", 191 | " \n", 192 | " \n", 193 | " \n", 194 | " \n", 195 | " \n", 196 | " \n", 197 | " \n", 198 | " \n", 199 | " \n", 200 | " \n", 201 | " \n", 202 | "
indexLabelbrandnamepricerankingredientsCombinationDryNormalOilySensitiveXY
00Moisturizer_CombinationLA MERCrème de la Mer1754.1Algae (Seaweed) Extract, Mineral Oil, Petrolat...111112.30312317.373549
11Moisturizer_CombinationSK-IIFacial Treatment Essence1794.1Galactomyces Ferment Filtrate (Pitera), Butyle...111117.593926-10.227859
22Moisturizer_CombinationDRUNK ELEPHANTProtini™ Polypeptide Cream684.4Water, Dicaprylyl Carbonate, Glycerin, Ceteary...11110-18.2002814.022318
33Moisturizer_CombinationLA MERThe Moisturizing Soft Cream1753.8Algae (Seaweed) Extract, Cyclopentasiloxane, P...1111132.755580-40.191727
44Moisturizer_CombinationIT COSMETICSYour Skin But Better™ CC+™ Cream with SPF 50+384.1Water, Snail Secretion Filtrate, Phenyl Trimet...11111-12.003542-57.294006
\n", 203 | "
" 204 | ], 205 | "text/plain": [ 206 | " index Label brand \\\n", 207 | "0 0 Moisturizer_Combination LA MER \n", 208 | "1 1 Moisturizer_Combination SK-II \n", 209 | "2 2 Moisturizer_Combination DRUNK ELEPHANT \n", 210 | "3 3 Moisturizer_Combination LA MER \n", 211 | "4 4 Moisturizer_Combination IT COSMETICS \n", 212 | "\n", 213 | " name price rank \\\n", 214 | "0 Crème de la Mer 175 4.1 \n", 215 | "1 Facial Treatment Essence 179 4.1 \n", 216 | "2 Protini™ Polypeptide Cream 68 4.4 \n", 217 | "3 The Moisturizing Soft Cream 175 3.8 \n", 218 | "4 Your Skin But Better™ CC+™ Cream with SPF 50+ 38 4.1 \n", 219 | "\n", 220 | " ingredients Combination Dry \\\n", 221 | "0 Algae (Seaweed) Extract, Mineral Oil, Petrolat... 1 1 \n", 222 | "1 Galactomyces Ferment Filtrate (Pitera), Butyle... 1 1 \n", 223 | "2 Water, Dicaprylyl Carbonate, Glycerin, Ceteary... 1 1 \n", 224 | "3 Algae (Seaweed) Extract, Cyclopentasiloxane, P... 1 1 \n", 225 | "4 Water, Snail Secretion Filtrate, Phenyl Trimet... 1 1 \n", 226 | "\n", 227 | " Normal Oily Sensitive X Y \n", 228 | "0 1 1 1 2.303123 17.373549 \n", 229 | "1 1 1 1 7.593926 -10.227859 \n", 230 | "2 1 1 0 -18.200281 4.022318 \n", 231 | "3 1 1 1 32.755580 -40.191727 \n", 232 | "4 1 1 1 -12.003542 -57.294006 " 233 | ] 234 | }, 235 | "execution_count": 18, 236 | "metadata": {}, 237 | "output_type": "execute_result" 238 | } 239 | ], 240 | "source": [ 241 | "df = pd.read_csv('data/cosmetic_TSNE.csv')\n", 242 | "df.head()" 243 | ] 244 | }, 245 | { 246 | "cell_type": "code", 247 | "execution_count": 29, 248 | "metadata": {}, 249 | "outputs": [ 250 | { 251 | "data": { 252 | "text/plain": [ 253 | "Index(['index', 'Label', 'brand', 'name', 'price', 'rank', 'ingredients',\n", 254 | " 'Combination', 'Dry', 'Normal', 'Oily', 'Sensitive', 'X', 'Y'],\n", 255 | " dtype='object')" 256 | ] 257 | }, 258 | "execution_count": 29, 259 | "metadata": {}, 260 | "output_type": "execute_result" 261 | } 262 | ], 263 | "source": [ 264 | "df.columns" 265 | ] 266 | }, 267 | { 268 | "cell_type": "markdown", 269 | "metadata": { 270 | "colab_type": "text", 271 | "id": "x-mu-NMhSsoz" 272 | }, 273 | "source": [ 274 | "All the steps until the decomposition is done already and I combine all data into one with all possible combination. `brand`, `name`, `price` and `rank` is the data of each item scraped from [Sephora](https://www.sephora.com). " 275 | ] 276 | }, 277 | { 278 | "cell_type": "code", 279 | "execution_count": 19, 280 | "metadata": { 281 | "colab": { 282 | "base_uri": "https://localhost:8080/", 283 | "height": 235 284 | }, 285 | "colab_type": "code", 286 | "id": "EFY5a6TzSso0", 287 | "outputId": "a2dcab65-60d4-4dbb-de8f-5050a9720a75" 288 | }, 289 | "outputs": [ 290 | { 291 | "data": { 292 | "text/plain": [ 293 | "array(['Moisturizer_Combination', 'Moisturizer_Dry', 'Moisturizer_Normal',\n", 294 | " 'Moisturizer_Oily', 'Moisturizer_Sensitive',\n", 295 | " 'Cleanser_Combination', 'Cleanser_Dry', 'Cleanser_Normal',\n", 296 | " 'Cleanser_Oily', 'Cleanser_Sensitive', 'Treatment_Combination',\n", 297 | " 'Treatment_Dry', 'Treatment_Normal', 'Treatment_Oily',\n", 298 | " 'Treatment_Sensitive', 'Face Mask_Combination', 'Face Mask_Dry',\n", 299 | " 'Face Mask_Normal', 'Face Mask_Oily', 'Face Mask_Sensitive',\n", 300 | " 'Eye cream_Combination', 'Eye cream_Dry', 'Eye cream_Normal',\n", 301 | " 'Eye cream_Oily', 'Eye cream_Sensitive', 'Sun protect_Combination',\n", 302 | " 'Sun protect_Dry', 'Sun protect_Normal', 'Sun protect_Oily',\n", 303 | " 'Sun protect_Sensitive'], dtype=object)" 304 | ] 305 | }, 306 | "execution_count": 19, 307 | "metadata": {}, 308 | "output_type": "execute_result" 309 | } 310 | ], 311 | "source": [ 312 | "# the 30 different combinations of options\n", 313 | "df.Label.unique()" 314 | ] 315 | }, 316 | { 317 | "cell_type": "code", 318 | "execution_count": 20, 319 | "metadata": { 320 | "colab": { 321 | "base_uri": "https://localhost:8080/", 322 | "height": 235 323 | }, 324 | "colab_type": "code", 325 | "id": "EFY5a6TzSso0", 326 | "outputId": "a2dcab65-60d4-4dbb-de8f-5050a9720a75" 327 | }, 328 | "outputs": [], 329 | "source": [ 330 | "# cosmetic filtering options \n", 331 | "option_1 = ['Moisturizer', 'Cleanser', 'Treatment', 'Face Mask', 'Eye cream', 'Sun protect']\n", 332 | "option_2 = ['Combination', 'Dry', 'Normal', 'Oily', 'Sensitive']" 333 | ] 334 | }, 335 | { 336 | "cell_type": "markdown", 337 | "metadata": { 338 | "colab_type": "text", 339 | "id": "2Lezcg4fSso6" 340 | }, 341 | "source": [ 342 | "There are 6 different categories of items and 5 skin tpye options. So `Label` column has all possible 30 combinations as above. To make a selecting option and filtering application on them, I calculated the similarities separately. Users can choice each one from option_1 and option_2 and get the filtered plot accordingly." 343 | ] 344 | }, 345 | { 346 | "cell_type": "markdown", 347 | "metadata": { 348 | "colab_type": "text", 349 | "id": "MuancOunSso7" 350 | }, 351 | "source": [ 352 | "## 2. Mapping with Bokeh" 353 | ] 354 | }, 355 | { 356 | "cell_type": "code", 357 | "execution_count": 21, 358 | "metadata": { 359 | "colab": { 360 | "base_uri": "https://localhost:8080/", 361 | "height": 47 362 | }, 363 | "colab_type": "code", 364 | "id": "FpLxrsdpSso9", 365 | "outputId": "da7c7e9e-549b-42f5-91fb-f84010c08686" 366 | }, 367 | "outputs": [ 368 | { 369 | "data": { 370 | "text/html": [ 371 | "\n", 372 | "
\n", 373 | " \n", 374 | " Loading BokehJS ...\n", 375 | "
" 376 | ] 377 | }, 378 | "metadata": {}, 379 | "output_type": "display_data" 380 | }, 381 | { 382 | "data": { 383 | "application/javascript": [ 384 | "\n", 385 | "(function(root) {\n", 386 | " function now() {\n", 387 | " return new Date();\n", 388 | " }\n", 389 | "\n", 390 | " var force = true;\n", 391 | "\n", 392 | " if (typeof (root._bokeh_onload_callbacks) === \"undefined\" || force === true) {\n", 393 | " root._bokeh_onload_callbacks = [];\n", 394 | " root._bokeh_is_loading = undefined;\n", 395 | " }\n", 396 | "\n", 397 | " var JS_MIME_TYPE = 'application/javascript';\n", 398 | " var HTML_MIME_TYPE = 'text/html';\n", 399 | " var EXEC_MIME_TYPE = 'application/vnd.bokehjs_exec.v0+json';\n", 400 | " var CLASS_NAME = 'output_bokeh rendered_html';\n", 401 | "\n", 402 | " /**\n", 403 | " * Render data to the DOM node\n", 404 | " */\n", 405 | " function render(props, node) {\n", 406 | " var script = document.createElement(\"script\");\n", 407 | " node.appendChild(script);\n", 408 | " }\n", 409 | "\n", 410 | " /**\n", 411 | " * Handle when an output is cleared or removed\n", 412 | " */\n", 413 | " function handleClearOutput(event, handle) {\n", 414 | " var cell = handle.cell;\n", 415 | "\n", 416 | " var id = cell.output_area._bokeh_element_id;\n", 417 | " var server_id = cell.output_area._bokeh_server_id;\n", 418 | " // Clean up Bokeh references\n", 419 | " if (id != null && id in Bokeh.index) {\n", 420 | " Bokeh.index[id].model.document.clear();\n", 421 | " delete Bokeh.index[id];\n", 422 | " }\n", 423 | "\n", 424 | " if (server_id !== undefined) {\n", 425 | " // Clean up Bokeh references\n", 426 | " var cmd = \"from bokeh.io.state import curstate; print(curstate().uuid_to_server['\" + server_id + \"'].get_sessions()[0].document.roots[0]._id)\";\n", 427 | " cell.notebook.kernel.execute(cmd, {\n", 428 | " iopub: {\n", 429 | " output: function(msg) {\n", 430 | " var id = msg.content.text.trim();\n", 431 | " if (id in Bokeh.index) {\n", 432 | " Bokeh.index[id].model.document.clear();\n", 433 | " delete Bokeh.index[id];\n", 434 | " }\n", 435 | " }\n", 436 | " }\n", 437 | " });\n", 438 | " // Destroy server and session\n", 439 | " var cmd = \"import bokeh.io.notebook as ion; ion.destroy_server('\" + server_id + \"')\";\n", 440 | " cell.notebook.kernel.execute(cmd);\n", 441 | " }\n", 442 | " }\n", 443 | "\n", 444 | " /**\n", 445 | " * Handle when a new output is added\n", 446 | " */\n", 447 | " function handleAddOutput(event, handle) {\n", 448 | " var output_area = handle.output_area;\n", 449 | " var output = handle.output;\n", 450 | "\n", 451 | " // limit handleAddOutput to display_data with EXEC_MIME_TYPE content only\n", 452 | " if ((output.output_type != \"display_data\") || (!output.data.hasOwnProperty(EXEC_MIME_TYPE))) {\n", 453 | " return\n", 454 | " }\n", 455 | "\n", 456 | " var toinsert = output_area.element.find(\".\" + CLASS_NAME.split(' ')[0]);\n", 457 | "\n", 458 | " if (output.metadata[EXEC_MIME_TYPE][\"id\"] !== undefined) {\n", 459 | " toinsert[toinsert.length - 1].firstChild.textContent = output.data[JS_MIME_TYPE];\n", 460 | " // store reference to embed id on output_area\n", 461 | " output_area._bokeh_element_id = output.metadata[EXEC_MIME_TYPE][\"id\"];\n", 462 | " }\n", 463 | " if (output.metadata[EXEC_MIME_TYPE][\"server_id\"] !== undefined) {\n", 464 | " var bk_div = document.createElement(\"div\");\n", 465 | " bk_div.innerHTML = output.data[HTML_MIME_TYPE];\n", 466 | " var script_attrs = bk_div.children[0].attributes;\n", 467 | " for (var i = 0; i < script_attrs.length; i++) {\n", 468 | " toinsert[toinsert.length - 1].firstChild.setAttribute(script_attrs[i].name, script_attrs[i].value);\n", 469 | " }\n", 470 | " // store reference to server id on output_area\n", 471 | " output_area._bokeh_server_id = output.metadata[EXEC_MIME_TYPE][\"server_id\"];\n", 472 | " }\n", 473 | " }\n", 474 | "\n", 475 | " function register_renderer(events, OutputArea) {\n", 476 | "\n", 477 | " function append_mime(data, metadata, element) {\n", 478 | " // create a DOM node to render to\n", 479 | " var toinsert = this.create_output_subarea(\n", 480 | " metadata,\n", 481 | " CLASS_NAME,\n", 482 | " EXEC_MIME_TYPE\n", 483 | " );\n", 484 | " this.keyboard_manager.register_events(toinsert);\n", 485 | " // Render to node\n", 486 | " var props = {data: data, metadata: metadata[EXEC_MIME_TYPE]};\n", 487 | " render(props, toinsert[toinsert.length - 1]);\n", 488 | " element.append(toinsert);\n", 489 | " return toinsert\n", 490 | " }\n", 491 | "\n", 492 | " /* Handle when an output is cleared or removed */\n", 493 | " events.on('clear_output.CodeCell', handleClearOutput);\n", 494 | " events.on('delete.Cell', handleClearOutput);\n", 495 | "\n", 496 | " /* Handle when a new output is added */\n", 497 | " events.on('output_added.OutputArea', handleAddOutput);\n", 498 | "\n", 499 | " /**\n", 500 | " * Register the mime type and append_mime function with output_area\n", 501 | " */\n", 502 | " OutputArea.prototype.register_mime_type(EXEC_MIME_TYPE, append_mime, {\n", 503 | " /* Is output safe? */\n", 504 | " safe: true,\n", 505 | " /* Index of renderer in `output_area.display_order` */\n", 506 | " index: 0\n", 507 | " });\n", 508 | " }\n", 509 | "\n", 510 | " // register the mime type if in Jupyter Notebook environment and previously unregistered\n", 511 | " if (root.Jupyter !== undefined) {\n", 512 | " var events = require('base/js/events');\n", 513 | " var OutputArea = require('notebook/js/outputarea').OutputArea;\n", 514 | "\n", 515 | " if (OutputArea.prototype.mime_types().indexOf(EXEC_MIME_TYPE) == -1) {\n", 516 | " register_renderer(events, OutputArea);\n", 517 | " }\n", 518 | " }\n", 519 | "\n", 520 | " \n", 521 | " if (typeof (root._bokeh_timeout) === \"undefined\" || force === true) {\n", 522 | " root._bokeh_timeout = Date.now() + 5000;\n", 523 | " root._bokeh_failed_load = false;\n", 524 | " }\n", 525 | "\n", 526 | " var NB_LOAD_WARNING = {'data': {'text/html':\n", 527 | " \"
\\n\"+\n", 528 | " \"

\\n\"+\n", 529 | " \"BokehJS does not appear to have successfully loaded. If loading BokehJS from CDN, this \\n\"+\n", 530 | " \"may be due to a slow or bad network connection. Possible fixes:\\n\"+\n", 531 | " \"

\\n\"+\n", 532 | " \"\\n\"+\n", 536 | " \"\\n\"+\n", 537 | " \"from bokeh.resources import INLINE\\n\"+\n", 538 | " \"output_notebook(resources=INLINE)\\n\"+\n", 539 | " \"\\n\"+\n", 540 | " \"
\"}};\n", 541 | "\n", 542 | " function display_loaded() {\n", 543 | " var el = document.getElementById(\"4ded3106-4e6a-46ed-ac95-b3d90dfd8690\");\n", 544 | " if (el != null) {\n", 545 | " el.textContent = \"BokehJS is loading...\";\n", 546 | " }\n", 547 | " if (root.Bokeh !== undefined) {\n", 548 | " if (el != null) {\n", 549 | " el.textContent = \"BokehJS \" + root.Bokeh.version + \" successfully loaded.\";\n", 550 | " }\n", 551 | " } else if (Date.now() < root._bokeh_timeout) {\n", 552 | " setTimeout(display_loaded, 100)\n", 553 | " }\n", 554 | " }\n", 555 | "\n", 556 | "\n", 557 | " function run_callbacks() {\n", 558 | " try {\n", 559 | " root._bokeh_onload_callbacks.forEach(function(callback) { callback() });\n", 560 | " }\n", 561 | " finally {\n", 562 | " delete root._bokeh_onload_callbacks\n", 563 | " }\n", 564 | " console.info(\"Bokeh: all callbacks have finished\");\n", 565 | " }\n", 566 | "\n", 567 | " function load_libs(js_urls, callback) {\n", 568 | " root._bokeh_onload_callbacks.push(callback);\n", 569 | " if (root._bokeh_is_loading > 0) {\n", 570 | " console.log(\"Bokeh: BokehJS is being loaded, scheduling callback at\", now());\n", 571 | " return null;\n", 572 | " }\n", 573 | " if (js_urls == null || js_urls.length === 0) {\n", 574 | " run_callbacks();\n", 575 | " return null;\n", 576 | " }\n", 577 | " console.log(\"Bokeh: BokehJS not loaded, scheduling load and callback at\", now());\n", 578 | " root._bokeh_is_loading = js_urls.length;\n", 579 | " for (var i = 0; i < js_urls.length; i++) {\n", 580 | " var url = js_urls[i];\n", 581 | " var s = document.createElement('script');\n", 582 | " s.src = url;\n", 583 | " s.async = false;\n", 584 | " s.onreadystatechange = s.onload = function() {\n", 585 | " root._bokeh_is_loading--;\n", 586 | " if (root._bokeh_is_loading === 0) {\n", 587 | " console.log(\"Bokeh: all BokehJS libraries loaded\");\n", 588 | " run_callbacks()\n", 589 | " }\n", 590 | " };\n", 591 | " s.onerror = function() {\n", 592 | " console.warn(\"failed to load library \" + url);\n", 593 | " };\n", 594 | " console.log(\"Bokeh: injecting script tag for BokehJS library: \", url);\n", 595 | " document.getElementsByTagName(\"head\")[0].appendChild(s);\n", 596 | " }\n", 597 | " };var element = document.getElementById(\"4ded3106-4e6a-46ed-ac95-b3d90dfd8690\");\n", 598 | " if (element == null) {\n", 599 | " console.log(\"Bokeh: ERROR: autoload.js configured with elementid '4ded3106-4e6a-46ed-ac95-b3d90dfd8690' but no matching script tag was found. \")\n", 600 | " return false;\n", 601 | " }\n", 602 | "\n", 603 | " var js_urls = [\"https://cdn.pydata.org/bokeh/release/bokeh-0.13.0.min.js\", \"https://cdn.pydata.org/bokeh/release/bokeh-widgets-0.13.0.min.js\", \"https://cdn.pydata.org/bokeh/release/bokeh-tables-0.13.0.min.js\", \"https://cdn.pydata.org/bokeh/release/bokeh-gl-0.13.0.min.js\"];\n", 604 | "\n", 605 | " var inline_js = [\n", 606 | " function(Bokeh) {\n", 607 | " Bokeh.set_log_level(\"info\");\n", 608 | " },\n", 609 | " \n", 610 | " function(Bokeh) {\n", 611 | " \n", 612 | " },\n", 613 | " function(Bokeh) {\n", 614 | " console.log(\"Bokeh: injecting CSS: https://cdn.pydata.org/bokeh/release/bokeh-0.13.0.min.css\");\n", 615 | " Bokeh.embed.inject_css(\"https://cdn.pydata.org/bokeh/release/bokeh-0.13.0.min.css\");\n", 616 | " console.log(\"Bokeh: injecting CSS: https://cdn.pydata.org/bokeh/release/bokeh-widgets-0.13.0.min.css\");\n", 617 | " Bokeh.embed.inject_css(\"https://cdn.pydata.org/bokeh/release/bokeh-widgets-0.13.0.min.css\");\n", 618 | " console.log(\"Bokeh: injecting CSS: https://cdn.pydata.org/bokeh/release/bokeh-tables-0.13.0.min.css\");\n", 619 | " Bokeh.embed.inject_css(\"https://cdn.pydata.org/bokeh/release/bokeh-tables-0.13.0.min.css\");\n", 620 | " }\n", 621 | " ];\n", 622 | "\n", 623 | " function run_inline_js() {\n", 624 | " \n", 625 | " if ((root.Bokeh !== undefined) || (force === true)) {\n", 626 | " for (var i = 0; i < inline_js.length; i++) {\n", 627 | " inline_js[i].call(root, root.Bokeh);\n", 628 | " }if (force === true) {\n", 629 | " display_loaded();\n", 630 | " }} else if (Date.now() < root._bokeh_timeout) {\n", 631 | " setTimeout(run_inline_js, 100);\n", 632 | " } else if (!root._bokeh_failed_load) {\n", 633 | " console.log(\"Bokeh: BokehJS failed to load within specified timeout.\");\n", 634 | " root._bokeh_failed_load = true;\n", 635 | " } else if (force !== true) {\n", 636 | " var cell = $(document.getElementById(\"4ded3106-4e6a-46ed-ac95-b3d90dfd8690\")).parents('.cell').data().cell;\n", 637 | " cell.output_area.append_execute_result(NB_LOAD_WARNING)\n", 638 | " }\n", 639 | "\n", 640 | " }\n", 641 | "\n", 642 | " if (root._bokeh_is_loading === 0) {\n", 643 | " console.log(\"Bokeh: BokehJS loaded, going straight to plotting\");\n", 644 | " run_inline_js();\n", 645 | " } else {\n", 646 | " load_libs(js_urls, function() {\n", 647 | " console.log(\"Bokeh: BokehJS plotting callback run at\", now());\n", 648 | " run_inline_js();\n", 649 | " });\n", 650 | " }\n", 651 | "}(window));" 652 | ], 653 | "application/vnd.bokehjs_load.v0+json": "\n(function(root) {\n function now() {\n return new Date();\n }\n\n var force = true;\n\n if (typeof (root._bokeh_onload_callbacks) === \"undefined\" || force === true) {\n root._bokeh_onload_callbacks = [];\n root._bokeh_is_loading = undefined;\n }\n\n \n\n \n if (typeof (root._bokeh_timeout) === \"undefined\" || force === true) {\n root._bokeh_timeout = Date.now() + 5000;\n root._bokeh_failed_load = false;\n }\n\n var NB_LOAD_WARNING = {'data': {'text/html':\n \"
\\n\"+\n \"

\\n\"+\n \"BokehJS does not appear to have successfully loaded. If loading BokehJS from CDN, this \\n\"+\n \"may be due to a slow or bad network connection. Possible fixes:\\n\"+\n \"

\\n\"+\n \"\\n\"+\n \"\\n\"+\n \"from bokeh.resources import INLINE\\n\"+\n \"output_notebook(resources=INLINE)\\n\"+\n \"\\n\"+\n \"
\"}};\n\n function display_loaded() {\n var el = document.getElementById(\"4ded3106-4e6a-46ed-ac95-b3d90dfd8690\");\n if (el != null) {\n el.textContent = \"BokehJS is loading...\";\n }\n if (root.Bokeh !== undefined) {\n if (el != null) {\n el.textContent = \"BokehJS \" + root.Bokeh.version + \" successfully loaded.\";\n }\n } else if (Date.now() < root._bokeh_timeout) {\n setTimeout(display_loaded, 100)\n }\n }\n\n\n function run_callbacks() {\n try {\n root._bokeh_onload_callbacks.forEach(function(callback) { callback() });\n }\n finally {\n delete root._bokeh_onload_callbacks\n }\n console.info(\"Bokeh: all callbacks have finished\");\n }\n\n function load_libs(js_urls, callback) {\n root._bokeh_onload_callbacks.push(callback);\n if (root._bokeh_is_loading > 0) {\n console.log(\"Bokeh: BokehJS is being loaded, scheduling callback at\", now());\n return null;\n }\n if (js_urls == null || js_urls.length === 0) {\n run_callbacks();\n return null;\n }\n console.log(\"Bokeh: BokehJS not loaded, scheduling load and callback at\", now());\n root._bokeh_is_loading = js_urls.length;\n for (var i = 0; i < js_urls.length; i++) {\n var url = js_urls[i];\n var s = document.createElement('script');\n s.src = url;\n s.async = false;\n s.onreadystatechange = s.onload = function() {\n root._bokeh_is_loading--;\n if (root._bokeh_is_loading === 0) {\n console.log(\"Bokeh: all BokehJS libraries loaded\");\n run_callbacks()\n }\n };\n s.onerror = function() {\n console.warn(\"failed to load library \" + url);\n };\n console.log(\"Bokeh: injecting script tag for BokehJS library: \", url);\n document.getElementsByTagName(\"head\")[0].appendChild(s);\n }\n };var element = document.getElementById(\"4ded3106-4e6a-46ed-ac95-b3d90dfd8690\");\n if (element == null) {\n console.log(\"Bokeh: ERROR: autoload.js configured with elementid '4ded3106-4e6a-46ed-ac95-b3d90dfd8690' but no matching script tag was found. \")\n return false;\n }\n\n var js_urls = [\"https://cdn.pydata.org/bokeh/release/bokeh-0.13.0.min.js\", \"https://cdn.pydata.org/bokeh/release/bokeh-widgets-0.13.0.min.js\", \"https://cdn.pydata.org/bokeh/release/bokeh-tables-0.13.0.min.js\", \"https://cdn.pydata.org/bokeh/release/bokeh-gl-0.13.0.min.js\"];\n\n var inline_js = [\n function(Bokeh) {\n Bokeh.set_log_level(\"info\");\n },\n \n function(Bokeh) {\n \n },\n function(Bokeh) {\n console.log(\"Bokeh: injecting CSS: https://cdn.pydata.org/bokeh/release/bokeh-0.13.0.min.css\");\n Bokeh.embed.inject_css(\"https://cdn.pydata.org/bokeh/release/bokeh-0.13.0.min.css\");\n console.log(\"Bokeh: injecting CSS: https://cdn.pydata.org/bokeh/release/bokeh-widgets-0.13.0.min.css\");\n Bokeh.embed.inject_css(\"https://cdn.pydata.org/bokeh/release/bokeh-widgets-0.13.0.min.css\");\n console.log(\"Bokeh: injecting CSS: https://cdn.pydata.org/bokeh/release/bokeh-tables-0.13.0.min.css\");\n Bokeh.embed.inject_css(\"https://cdn.pydata.org/bokeh/release/bokeh-tables-0.13.0.min.css\");\n }\n ];\n\n function run_inline_js() {\n \n if ((root.Bokeh !== undefined) || (force === true)) {\n for (var i = 0; i < inline_js.length; i++) {\n inline_js[i].call(root, root.Bokeh);\n }if (force === true) {\n display_loaded();\n }} else if (Date.now() < root._bokeh_timeout) {\n setTimeout(run_inline_js, 100);\n } else if (!root._bokeh_failed_load) {\n console.log(\"Bokeh: BokehJS failed to load within specified timeout.\");\n root._bokeh_failed_load = true;\n } else if (force !== true) {\n var cell = $(document.getElementById(\"4ded3106-4e6a-46ed-ac95-b3d90dfd8690\")).parents('.cell').data().cell;\n cell.output_area.append_execute_result(NB_LOAD_WARNING)\n }\n\n }\n\n if (root._bokeh_is_loading === 0) {\n console.log(\"Bokeh: BokehJS loaded, going straight to plotting\");\n run_inline_js();\n } else {\n load_libs(js_urls, function() {\n console.log(\"Bokeh: BokehJS plotting callback run at\", now());\n run_inline_js();\n });\n }\n}(window));" 654 | }, 655 | "metadata": {}, 656 | "output_type": "display_data" 657 | } 658 | ], 659 | "source": [ 660 | "output_notebook()" 661 | ] 662 | }, 663 | { 664 | "cell_type": "markdown", 665 | "metadata": { 666 | "colab_type": "text", 667 | "id": "W-pe48v3SspC" 668 | }, 669 | "source": [ 670 | "To work with Bokeh server on jupyter notebook, made a connection first." 671 | ] 672 | }, 673 | { 674 | "cell_type": "code", 675 | "execution_count": 27, 676 | "metadata": { 677 | "colab": {}, 678 | "colab_type": "code", 679 | "id": "0S5Ju8pQSspD" 680 | }, 681 | "outputs": [], 682 | "source": [ 683 | "# make a source and scatter bokeh plot \n", 684 | "source = ColumnDataSource(df)\n", 685 | "plot = figure(x_axis_label = 'T-SNE 1', y_axis_label = 'T-SNE 2', \n", 686 | " width = 500, height = 400)\n", 687 | "plot.circle(x = 'X', y = 'Y', source = source, \n", 688 | " size = 10, color = '#FF7373', alpha = .8)\n", 689 | "\n", 690 | "plot.background_fill_color = \"beige\"\n", 691 | "plot.background_fill_alpha = 0.2\n", 692 | "\n", 693 | "# add hover tool\n", 694 | "hover = HoverTool(tooltips = [\n", 695 | " ('Item', '@name'),\n", 696 | " ('brand', '@brand'),\n", 697 | " ('Price', '$ @price'),\n", 698 | " ('Rank', '@rank')])\n", 699 | "plot.add_tools(hover)" 700 | ] 701 | }, 702 | { 703 | "cell_type": "code", 704 | "execution_count": 30, 705 | "metadata": { 706 | "colab": {}, 707 | "colab_type": "code", 708 | "id": "MZKutfE6SspK" 709 | }, 710 | "outputs": [], 711 | "source": [ 712 | "# define the callback\n", 713 | "def update(op1 = option_1[0], op2 = option_2[0]):\n", 714 | " a_b = op1 + '_' + op2\n", 715 | " new_data = {\n", 716 | " 'X' : df[df['Label'] == a_b]['X'],\n", 717 | " 'Y' : df[df['Label'] == a_b]['Y'],\n", 718 | " 'name' : df[df['Label'] == a_b]['name'],\n", 719 | " 'brand' : df[df['Label'] == a_b]['brand'],\n", 720 | " 'price' : df[df['Label'] == a_b]['price'],\n", 721 | " 'rank' : df[df['Label'] == a_b]['rank'],\n", 722 | " }\n", 723 | " source.data = new_data\n", 724 | " push_notebook() " 725 | ] 726 | }, 727 | { 728 | "cell_type": "code", 729 | "execution_count": 31, 730 | "metadata": { 731 | "colab": {}, 732 | "colab_type": "code", 733 | "id": "MZKutfE6SspK" 734 | }, 735 | "outputs": [ 736 | { 737 | "data": { 738 | "text/html": [ 739 | "\n", 740 | "
\n", 741 | " \n", 742 | " Loading BokehJS ...\n", 743 | "
" 744 | ] 745 | }, 746 | "metadata": {}, 747 | "output_type": "display_data" 748 | }, 749 | { 750 | "data": { 751 | "application/javascript": [ 752 | "\n", 753 | "(function(root) {\n", 754 | " function now() {\n", 755 | " return new Date();\n", 756 | " }\n", 757 | "\n", 758 | " var force = true;\n", 759 | "\n", 760 | " if (typeof (root._bokeh_onload_callbacks) === \"undefined\" || force === true) {\n", 761 | " root._bokeh_onload_callbacks = [];\n", 762 | " root._bokeh_is_loading = undefined;\n", 763 | " }\n", 764 | "\n", 765 | " var JS_MIME_TYPE = 'application/javascript';\n", 766 | " var HTML_MIME_TYPE = 'text/html';\n", 767 | " var EXEC_MIME_TYPE = 'application/vnd.bokehjs_exec.v0+json';\n", 768 | " var CLASS_NAME = 'output_bokeh rendered_html';\n", 769 | "\n", 770 | " /**\n", 771 | " * Render data to the DOM node\n", 772 | " */\n", 773 | " function render(props, node) {\n", 774 | " var script = document.createElement(\"script\");\n", 775 | " node.appendChild(script);\n", 776 | " }\n", 777 | "\n", 778 | " /**\n", 779 | " * Handle when an output is cleared or removed\n", 780 | " */\n", 781 | " function handleClearOutput(event, handle) {\n", 782 | " var cell = handle.cell;\n", 783 | "\n", 784 | " var id = cell.output_area._bokeh_element_id;\n", 785 | " var server_id = cell.output_area._bokeh_server_id;\n", 786 | " // Clean up Bokeh references\n", 787 | " if (id != null && id in Bokeh.index) {\n", 788 | " Bokeh.index[id].model.document.clear();\n", 789 | " delete Bokeh.index[id];\n", 790 | " }\n", 791 | "\n", 792 | " if (server_id !== undefined) {\n", 793 | " // Clean up Bokeh references\n", 794 | " var cmd = \"from bokeh.io.state import curstate; print(curstate().uuid_to_server['\" + server_id + \"'].get_sessions()[0].document.roots[0]._id)\";\n", 795 | " cell.notebook.kernel.execute(cmd, {\n", 796 | " iopub: {\n", 797 | " output: function(msg) {\n", 798 | " var id = msg.content.text.trim();\n", 799 | " if (id in Bokeh.index) {\n", 800 | " Bokeh.index[id].model.document.clear();\n", 801 | " delete Bokeh.index[id];\n", 802 | " }\n", 803 | " }\n", 804 | " }\n", 805 | " });\n", 806 | " // Destroy server and session\n", 807 | " var cmd = \"import bokeh.io.notebook as ion; ion.destroy_server('\" + server_id + \"')\";\n", 808 | " cell.notebook.kernel.execute(cmd);\n", 809 | " }\n", 810 | " }\n", 811 | "\n", 812 | " /**\n", 813 | " * Handle when a new output is added\n", 814 | " */\n", 815 | " function handleAddOutput(event, handle) {\n", 816 | " var output_area = handle.output_area;\n", 817 | " var output = handle.output;\n", 818 | "\n", 819 | " // limit handleAddOutput to display_data with EXEC_MIME_TYPE content only\n", 820 | " if ((output.output_type != \"display_data\") || (!output.data.hasOwnProperty(EXEC_MIME_TYPE))) {\n", 821 | " return\n", 822 | " }\n", 823 | "\n", 824 | " var toinsert = output_area.element.find(\".\" + CLASS_NAME.split(' ')[0]);\n", 825 | "\n", 826 | " if (output.metadata[EXEC_MIME_TYPE][\"id\"] !== undefined) {\n", 827 | " toinsert[toinsert.length - 1].firstChild.textContent = output.data[JS_MIME_TYPE];\n", 828 | " // store reference to embed id on output_area\n", 829 | " output_area._bokeh_element_id = output.metadata[EXEC_MIME_TYPE][\"id\"];\n", 830 | " }\n", 831 | " if (output.metadata[EXEC_MIME_TYPE][\"server_id\"] !== undefined) {\n", 832 | " var bk_div = document.createElement(\"div\");\n", 833 | " bk_div.innerHTML = output.data[HTML_MIME_TYPE];\n", 834 | " var script_attrs = bk_div.children[0].attributes;\n", 835 | " for (var i = 0; i < script_attrs.length; i++) {\n", 836 | " toinsert[toinsert.length - 1].firstChild.setAttribute(script_attrs[i].name, script_attrs[i].value);\n", 837 | " }\n", 838 | " // store reference to server id on output_area\n", 839 | " output_area._bokeh_server_id = output.metadata[EXEC_MIME_TYPE][\"server_id\"];\n", 840 | " }\n", 841 | " }\n", 842 | "\n", 843 | " function register_renderer(events, OutputArea) {\n", 844 | "\n", 845 | " function append_mime(data, metadata, element) {\n", 846 | " // create a DOM node to render to\n", 847 | " var toinsert = this.create_output_subarea(\n", 848 | " metadata,\n", 849 | " CLASS_NAME,\n", 850 | " EXEC_MIME_TYPE\n", 851 | " );\n", 852 | " this.keyboard_manager.register_events(toinsert);\n", 853 | " // Render to node\n", 854 | " var props = {data: data, metadata: metadata[EXEC_MIME_TYPE]};\n", 855 | " render(props, toinsert[toinsert.length - 1]);\n", 856 | " element.append(toinsert);\n", 857 | " return toinsert\n", 858 | " }\n", 859 | "\n", 860 | " /* Handle when an output is cleared or removed */\n", 861 | " events.on('clear_output.CodeCell', handleClearOutput);\n", 862 | " events.on('delete.Cell', handleClearOutput);\n", 863 | "\n", 864 | " /* Handle when a new output is added */\n", 865 | " events.on('output_added.OutputArea', handleAddOutput);\n", 866 | "\n", 867 | " /**\n", 868 | " * Register the mime type and append_mime function with output_area\n", 869 | " */\n", 870 | " OutputArea.prototype.register_mime_type(EXEC_MIME_TYPE, append_mime, {\n", 871 | " /* Is output safe? */\n", 872 | " safe: true,\n", 873 | " /* Index of renderer in `output_area.display_order` */\n", 874 | " index: 0\n", 875 | " });\n", 876 | " }\n", 877 | "\n", 878 | " // register the mime type if in Jupyter Notebook environment and previously unregistered\n", 879 | " if (root.Jupyter !== undefined) {\n", 880 | " var events = require('base/js/events');\n", 881 | " var OutputArea = require('notebook/js/outputarea').OutputArea;\n", 882 | "\n", 883 | " if (OutputArea.prototype.mime_types().indexOf(EXEC_MIME_TYPE) == -1) {\n", 884 | " register_renderer(events, OutputArea);\n", 885 | " }\n", 886 | " }\n", 887 | "\n", 888 | " \n", 889 | " if (typeof (root._bokeh_timeout) === \"undefined\" || force === true) {\n", 890 | " root._bokeh_timeout = Date.now() + 5000;\n", 891 | " root._bokeh_failed_load = false;\n", 892 | " }\n", 893 | "\n", 894 | " var NB_LOAD_WARNING = {'data': {'text/html':\n", 895 | " \"
\\n\"+\n", 896 | " \"

\\n\"+\n", 897 | " \"BokehJS does not appear to have successfully loaded. If loading BokehJS from CDN, this \\n\"+\n", 898 | " \"may be due to a slow or bad network connection. Possible fixes:\\n\"+\n", 899 | " \"

\\n\"+\n", 900 | " \"\\n\"+\n", 904 | " \"\\n\"+\n", 905 | " \"from bokeh.resources import INLINE\\n\"+\n", 906 | " \"output_notebook(resources=INLINE)\\n\"+\n", 907 | " \"\\n\"+\n", 908 | " \"
\"}};\n", 909 | "\n", 910 | " function display_loaded() {\n", 911 | " var el = document.getElementById(\"8db25678-675c-4fac-9aa2-cc7953b820b9\");\n", 912 | " if (el != null) {\n", 913 | " el.textContent = \"BokehJS is loading...\";\n", 914 | " }\n", 915 | " if (root.Bokeh !== undefined) {\n", 916 | " if (el != null) {\n", 917 | " el.textContent = \"BokehJS \" + root.Bokeh.version + \" successfully loaded.\";\n", 918 | " }\n", 919 | " } else if (Date.now() < root._bokeh_timeout) {\n", 920 | " setTimeout(display_loaded, 100)\n", 921 | " }\n", 922 | " }\n", 923 | "\n", 924 | "\n", 925 | " function run_callbacks() {\n", 926 | " try {\n", 927 | " root._bokeh_onload_callbacks.forEach(function(callback) { callback() });\n", 928 | " }\n", 929 | " finally {\n", 930 | " delete root._bokeh_onload_callbacks\n", 931 | " }\n", 932 | " console.info(\"Bokeh: all callbacks have finished\");\n", 933 | " }\n", 934 | "\n", 935 | " function load_libs(js_urls, callback) {\n", 936 | " root._bokeh_onload_callbacks.push(callback);\n", 937 | " if (root._bokeh_is_loading > 0) {\n", 938 | " console.log(\"Bokeh: BokehJS is being loaded, scheduling callback at\", now());\n", 939 | " return null;\n", 940 | " }\n", 941 | " if (js_urls == null || js_urls.length === 0) {\n", 942 | " run_callbacks();\n", 943 | " return null;\n", 944 | " }\n", 945 | " console.log(\"Bokeh: BokehJS not loaded, scheduling load and callback at\", now());\n", 946 | " root._bokeh_is_loading = js_urls.length;\n", 947 | " for (var i = 0; i < js_urls.length; i++) {\n", 948 | " var url = js_urls[i];\n", 949 | " var s = document.createElement('script');\n", 950 | " s.src = url;\n", 951 | " s.async = false;\n", 952 | " s.onreadystatechange = s.onload = function() {\n", 953 | " root._bokeh_is_loading--;\n", 954 | " if (root._bokeh_is_loading === 0) {\n", 955 | " console.log(\"Bokeh: all BokehJS libraries loaded\");\n", 956 | " run_callbacks()\n", 957 | " }\n", 958 | " };\n", 959 | " s.onerror = function() {\n", 960 | " console.warn(\"failed to load library \" + url);\n", 961 | " };\n", 962 | " console.log(\"Bokeh: injecting script tag for BokehJS library: \", url);\n", 963 | " document.getElementsByTagName(\"head\")[0].appendChild(s);\n", 964 | " }\n", 965 | " };var element = document.getElementById(\"8db25678-675c-4fac-9aa2-cc7953b820b9\");\n", 966 | " if (element == null) {\n", 967 | " console.log(\"Bokeh: ERROR: autoload.js configured with elementid '8db25678-675c-4fac-9aa2-cc7953b820b9' but no matching script tag was found. \")\n", 968 | " return false;\n", 969 | " }\n", 970 | "\n", 971 | " var js_urls = [\"https://cdn.pydata.org/bokeh/release/bokeh-0.13.0.min.js\", \"https://cdn.pydata.org/bokeh/release/bokeh-widgets-0.13.0.min.js\", \"https://cdn.pydata.org/bokeh/release/bokeh-tables-0.13.0.min.js\", \"https://cdn.pydata.org/bokeh/release/bokeh-gl-0.13.0.min.js\"];\n", 972 | "\n", 973 | " var inline_js = [\n", 974 | " function(Bokeh) {\n", 975 | " Bokeh.set_log_level(\"info\");\n", 976 | " },\n", 977 | " \n", 978 | " function(Bokeh) {\n", 979 | " \n", 980 | " },\n", 981 | " function(Bokeh) {\n", 982 | " console.log(\"Bokeh: injecting CSS: https://cdn.pydata.org/bokeh/release/bokeh-0.13.0.min.css\");\n", 983 | " Bokeh.embed.inject_css(\"https://cdn.pydata.org/bokeh/release/bokeh-0.13.0.min.css\");\n", 984 | " console.log(\"Bokeh: injecting CSS: https://cdn.pydata.org/bokeh/release/bokeh-widgets-0.13.0.min.css\");\n", 985 | " Bokeh.embed.inject_css(\"https://cdn.pydata.org/bokeh/release/bokeh-widgets-0.13.0.min.css\");\n", 986 | " console.log(\"Bokeh: injecting CSS: https://cdn.pydata.org/bokeh/release/bokeh-tables-0.13.0.min.css\");\n", 987 | " Bokeh.embed.inject_css(\"https://cdn.pydata.org/bokeh/release/bokeh-tables-0.13.0.min.css\");\n", 988 | " }\n", 989 | " ];\n", 990 | "\n", 991 | " function run_inline_js() {\n", 992 | " \n", 993 | " if ((root.Bokeh !== undefined) || (force === true)) {\n", 994 | " for (var i = 0; i < inline_js.length; i++) {\n", 995 | " inline_js[i].call(root, root.Bokeh);\n", 996 | " }if (force === true) {\n", 997 | " display_loaded();\n", 998 | " }} else if (Date.now() < root._bokeh_timeout) {\n", 999 | " setTimeout(run_inline_js, 100);\n", 1000 | " } else if (!root._bokeh_failed_load) {\n", 1001 | " console.log(\"Bokeh: BokehJS failed to load within specified timeout.\");\n", 1002 | " root._bokeh_failed_load = true;\n", 1003 | " } else if (force !== true) {\n", 1004 | " var cell = $(document.getElementById(\"8db25678-675c-4fac-9aa2-cc7953b820b9\")).parents('.cell').data().cell;\n", 1005 | " cell.output_area.append_execute_result(NB_LOAD_WARNING)\n", 1006 | " }\n", 1007 | "\n", 1008 | " }\n", 1009 | "\n", 1010 | " if (root._bokeh_is_loading === 0) {\n", 1011 | " console.log(\"Bokeh: BokehJS loaded, going straight to plotting\");\n", 1012 | " run_inline_js();\n", 1013 | " } else {\n", 1014 | " load_libs(js_urls, function() {\n", 1015 | " console.log(\"Bokeh: BokehJS plotting callback run at\", now());\n", 1016 | " run_inline_js();\n", 1017 | " });\n", 1018 | " }\n", 1019 | "}(window));" 1020 | ], 1021 | "application/vnd.bokehjs_load.v0+json": "\n(function(root) {\n function now() {\n return new Date();\n }\n\n var force = true;\n\n if (typeof (root._bokeh_onload_callbacks) === \"undefined\" || force === true) {\n root._bokeh_onload_callbacks = [];\n root._bokeh_is_loading = undefined;\n }\n\n \n\n \n if (typeof (root._bokeh_timeout) === \"undefined\" || force === true) {\n root._bokeh_timeout = Date.now() + 5000;\n root._bokeh_failed_load = false;\n }\n\n var NB_LOAD_WARNING = {'data': {'text/html':\n \"
\\n\"+\n \"

\\n\"+\n \"BokehJS does not appear to have successfully loaded. If loading BokehJS from CDN, this \\n\"+\n \"may be due to a slow or bad network connection. Possible fixes:\\n\"+\n \"

\\n\"+\n \"\\n\"+\n \"\\n\"+\n \"from bokeh.resources import INLINE\\n\"+\n \"output_notebook(resources=INLINE)\\n\"+\n \"\\n\"+\n \"
\"}};\n\n function display_loaded() {\n var el = document.getElementById(\"8db25678-675c-4fac-9aa2-cc7953b820b9\");\n if (el != null) {\n el.textContent = \"BokehJS is loading...\";\n }\n if (root.Bokeh !== undefined) {\n if (el != null) {\n el.textContent = \"BokehJS \" + root.Bokeh.version + \" successfully loaded.\";\n }\n } else if (Date.now() < root._bokeh_timeout) {\n setTimeout(display_loaded, 100)\n }\n }\n\n\n function run_callbacks() {\n try {\n root._bokeh_onload_callbacks.forEach(function(callback) { callback() });\n }\n finally {\n delete root._bokeh_onload_callbacks\n }\n console.info(\"Bokeh: all callbacks have finished\");\n }\n\n function load_libs(js_urls, callback) {\n root._bokeh_onload_callbacks.push(callback);\n if (root._bokeh_is_loading > 0) {\n console.log(\"Bokeh: BokehJS is being loaded, scheduling callback at\", now());\n return null;\n }\n if (js_urls == null || js_urls.length === 0) {\n run_callbacks();\n return null;\n }\n console.log(\"Bokeh: BokehJS not loaded, scheduling load and callback at\", now());\n root._bokeh_is_loading = js_urls.length;\n for (var i = 0; i < js_urls.length; i++) {\n var url = js_urls[i];\n var s = document.createElement('script');\n s.src = url;\n s.async = false;\n s.onreadystatechange = s.onload = function() {\n root._bokeh_is_loading--;\n if (root._bokeh_is_loading === 0) {\n console.log(\"Bokeh: all BokehJS libraries loaded\");\n run_callbacks()\n }\n };\n s.onerror = function() {\n console.warn(\"failed to load library \" + url);\n };\n console.log(\"Bokeh: injecting script tag for BokehJS library: \", url);\n document.getElementsByTagName(\"head\")[0].appendChild(s);\n }\n };var element = document.getElementById(\"8db25678-675c-4fac-9aa2-cc7953b820b9\");\n if (element == null) {\n console.log(\"Bokeh: ERROR: autoload.js configured with elementid '8db25678-675c-4fac-9aa2-cc7953b820b9' but no matching script tag was found. \")\n return false;\n }\n\n var js_urls = [\"https://cdn.pydata.org/bokeh/release/bokeh-0.13.0.min.js\", \"https://cdn.pydata.org/bokeh/release/bokeh-widgets-0.13.0.min.js\", \"https://cdn.pydata.org/bokeh/release/bokeh-tables-0.13.0.min.js\", \"https://cdn.pydata.org/bokeh/release/bokeh-gl-0.13.0.min.js\"];\n\n var inline_js = [\n function(Bokeh) {\n Bokeh.set_log_level(\"info\");\n },\n \n function(Bokeh) {\n \n },\n function(Bokeh) {\n console.log(\"Bokeh: injecting CSS: https://cdn.pydata.org/bokeh/release/bokeh-0.13.0.min.css\");\n Bokeh.embed.inject_css(\"https://cdn.pydata.org/bokeh/release/bokeh-0.13.0.min.css\");\n console.log(\"Bokeh: injecting CSS: https://cdn.pydata.org/bokeh/release/bokeh-widgets-0.13.0.min.css\");\n Bokeh.embed.inject_css(\"https://cdn.pydata.org/bokeh/release/bokeh-widgets-0.13.0.min.css\");\n console.log(\"Bokeh: injecting CSS: https://cdn.pydata.org/bokeh/release/bokeh-tables-0.13.0.min.css\");\n Bokeh.embed.inject_css(\"https://cdn.pydata.org/bokeh/release/bokeh-tables-0.13.0.min.css\");\n }\n ];\n\n function run_inline_js() {\n \n if ((root.Bokeh !== undefined) || (force === true)) {\n for (var i = 0; i < inline_js.length; i++) {\n inline_js[i].call(root, root.Bokeh);\n }if (force === true) {\n display_loaded();\n }} else if (Date.now() < root._bokeh_timeout) {\n setTimeout(run_inline_js, 100);\n } else if (!root._bokeh_failed_load) {\n console.log(\"Bokeh: BokehJS failed to load within specified timeout.\");\n root._bokeh_failed_load = true;\n } else if (force !== true) {\n var cell = $(document.getElementById(\"8db25678-675c-4fac-9aa2-cc7953b820b9\")).parents('.cell').data().cell;\n cell.output_area.append_execute_result(NB_LOAD_WARNING)\n }\n\n }\n\n if (root._bokeh_is_loading === 0) {\n console.log(\"Bokeh: BokehJS loaded, going straight to plotting\");\n run_inline_js();\n } else {\n load_libs(js_urls, function() {\n console.log(\"Bokeh: BokehJS plotting callback run at\", now());\n run_inline_js();\n });\n }\n}(window));" 1022 | }, 1023 | "metadata": {}, 1024 | "output_type": "display_data" 1025 | }, 1026 | { 1027 | "data": { 1028 | "application/vnd.jupyter.widget-view+json": { 1029 | "model_id": "1e80bf9b5ca04ae0a37e0c51fcd8b9fc", 1030 | "version_major": 2, 1031 | "version_minor": 0 1032 | }, 1033 | "text/plain": [ 1034 | "interactive(children=(Dropdown(description='op1', options=('Moisturizer', 'Cleanser', 'Treatment', 'Face Mask'…" 1035 | ] 1036 | }, 1037 | "metadata": {}, 1038 | "output_type": "display_data" 1039 | }, 1040 | { 1041 | "data": { 1042 | "text/html": [ 1043 | "\n", 1044 | "\n", 1045 | "\n", 1046 | "\n", 1047 | "\n", 1048 | "\n", 1049 | "
\n" 1050 | ] 1051 | }, 1052 | "metadata": {}, 1053 | "output_type": "display_data" 1054 | }, 1055 | { 1056 | "data": { 1057 | "application/javascript": [ 1058 | "(function(root) {\n", 1059 | " function embed_document(root) {\n", 1060 | " \n", 1061 | " var docs_json = {\"1e2b04ec-9534-4f15-b7ae-31aa723bf253\":{\"roots\":{\"references\":[{\"attributes\":{},\"id\":\"050952e4-4d41-4b9c-a8cf-1c02207a3655\",\"type\":\"LinearScale\"},{\"attributes\":{\"callback\":null,\"renderers\":\"auto\",\"tooltips\":[[\"Item\",\"@name\"],[\"brand\",\"@brand\"],[\"Price\",\"$ @price\"],[\"Rank\",\"@rank\"]]},\"id\":\"f06d5197-a3fa-4a75-b659-5dc31c3d7f6e\",\"type\":\"HoverTool\"},{\"attributes\":{},\"id\":\"7a253c00-9ec0-49c2-a819-1a772416cb5b\",\"type\":\"WheelZoomTool\"},{\"attributes\":{\"fill_alpha\":{\"value\":0.1},\"fill_color\":{\"value\":\"#1f77b4\"},\"line_alpha\":{\"value\":0.1},\"line_color\":{\"value\":\"#1f77b4\"},\"size\":{\"units\":\"screen\",\"value\":10},\"x\":{\"field\":\"X\"},\"y\":{\"field\":\"Y\"}},\"id\":\"77f9f060-adc9-402c-b897-47843f998afe\",\"type\":\"Circle\"},{\"attributes\":{},\"id\":\"8254987c-8219-444c-b0b3-67d2f1bb45a5\",\"type\":\"BasicTicker\"},{\"attributes\":{\"callback\":null,\"data\":{\"X\":{\"__ndarray__\":\"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\",\"dtype\":\"float64\",\"shape\":[199]},\"Y\":{\"__ndarray__\":\"GXRC6KBfMUDTMlLvqXQkwG1leKLaFhBATmGlgooYRMD1aRX9oaVMwLG33oDgKxjAkzoBTYQtRMDOwTOhSboaQF1Q3zKnqxLA5jquwj0h67/S+8bXnkE1QEJiu3uAPilAcRQP3g0oHEB4QNmUKyBDwGwotRfRJizA7Bfshm1PREAWU+knnN0wQDz1SIPbUjbAb6cIyzOK2L8uHAjJAoJGwMO4G0Rr+T5APJRXnYvlE0CI83AC0ykmQCUSlsLN0wZAn4g04OJnCsC1xTU+kxUywMTSwI9qMCDA0Jfe/lwKRUDwpfCg2cFBQKxHhsGhpB/A8icqG9bwMEALSPsfYIU0QA74/DBCSDFASIjyBS0sOEBMxoxLsLP8P0K4Agr1FPo/dw/QfTlbEUB4WPLC50gbwNij7ARmy8w/lYWvr3VhK0D1fThIiD46QFt8CoDxmDTARZ4kXTP9Q0AtPgXAeHo1wCR4QxoVaCzAHtrHCn6DLEA0SwLU1D5FQDAdhMoJJKq/hnR4COOnD0Cp0Eid45DYv6lqgqj7UEFAIgIOoUodSsB72uGvydpEQG3fo/56oTLAn+V5cHcWNkCyYyMQrxMwwM2Pv7Sopy7ASdHKvcBcOsDrHW6HhvExQPqL6r6txRHAbSHIQQm7J8Ch8Nk6OPwxwDkroib6fCVAFva0w1/rQEAx8Nx7uKQ8wOlDF9S3FDJAK0DKPn6F8b8OMPMd/Hw0wHj28MBbAxRAp6CRvv/FC0C7s3bbhepAwJYmpaDbyzpAzT6PUZ4xIsA6OxkcJbs7wJ93Y0FhrDtAeFp+4CpHO8DkDwaee/8/QKclVkYjnypAYw/tYwV/GUA/48KBkFwnQD3UtmEU/CHAz0vFxrzOIsA3iqw1lKoWQP1nzY+/nCxAZLz3gfXJFUDeOZShKsIwwIOrlzojBfE/j2rY74ltIUB0zHnGvjwxQKvTgaynVv+/O8eA7PUSQcCeKt8zEuktwIRKXMe4mjjAzHwHP3HwIMAGZoUi3ZcawELRPIBFPhZAvHfUmBB7OMB3oE55dOs3wMZoHVVNADZAb5o+O+D6OUDJkc7AyA80wA6jIHh8WyHAJJhqZi1NL8Am4UIewfE4QABw7Nlz4TVAoaAUrdzTJ0DRsBh1rf0QQDQY2YRqWQ3A3H9kOnQ6A0Ad5PVgUnxEQNuIJ7uZmSJAu5f75ChcNMDvGYnQCG4swE/gQsMHyw9A2o6pu7LDMcCFz9bBwVJDwDlAMEePI0FAjRAebRxxK0AvqG+Z01lAQKmPl6BMpOm/4MDkRpENN8B7D5ccd8pEwOjc7XppRjBAFCNL5lhWPsDGMv0S8XYCQMZrXtVZhSdA5q+QuTJIL8CHU+bmG1ErwDDi8aPQWAfA3TqkvWa2FkBWJZF9kHE1QLXo+MMqeQRAJJhqZi2hPEBOWfIdoSMTQCv2l92TuzFAkrOwpx1OPsCMTMCvkSQXwFV9U/3v7f4//wWCABmCIcDgKk8g7LwgQBB4YADhCzVA/OHnvwcfRUD6muWy0WkcQL5ubNFdLeg/o3VUNUFsKcCNQpJZvUMjQA4w8x38zBDA1pC4x9IXKcCF0EGXcEgEwO5D3nL1mylAzpIANbWEOsA7/DVZo04pQOhBMP0BZQvAoyWPp+VnNUDy7V2DvpQ0wNClbh3SLh/A8MNBQpT3OcAbCwqDMr0pQLyt9NpsjDLAB3qobcOIKcBuADYgQpwqQD55WKg1GTvA6sk+wEHY6z+SCI1g49o+QAJxaNt6dwXAJT53gv0DPEC5xJEHIj81QG1y+KQTiS3AQfLOoQzVQMDlmCzuPxohwJjuZCYHQva/U7RyLzADIECxFp8CYNgwwFUwKqkTWCNAINJvXwfuA8BG6j2V0z4SwGX+0Tdp/jxANNdppKU6NkDHLebnhmYhQHPzjeieNRhAuHTMecYOM0CjBWhbzXImwOC4jJsaQDxAyeaqeY7INMBoX3mQnkoiQLlEP2Tb4wvArRbYYyKtIUBLJCyFm2cOwIo+H2XEZSbAuUuH8xVp+D8re1xltwD9v0v5tZoaDfs/sVBrmndgP8DHEtbG2GEnwOlCrP4IpznAhOV0/pJxHMCGBmLZzNkrQC9U/rW8GiDA1RXH3BAuG0A=\",\"dtype\":\"float64\",\"shape\":[199]},\"brand\":[\"LA MER\",\"SK-II\",\"DRUNK ELEPHANT\",\"LA MER\",\"IT COSMETICS\",\"TATCHA\",\"DRUNK ELEPHANT\",\"DRUNK ELEPHANT\",\"KIEHL'S SINCE 1851\",\"KIEHL'S SINCE 1851\",\"BELIF\",\"SUNDAY RILEY\",\"FARMACY\",\"DRUNK ELEPHANT\",\"FIRST AID BEAUTY\",\"CLINIQUE\",\"LA MER\",\"FRESH\",\"DRUNK ELEPHANT\",\"ORIGINS\",\"CLINIQUE\",\"SK-II\",\"TATCHA\",\"OLEHENRIKSEN\",\"IT COSMETICS\",\"LANEIGE\",\"SK-II\",\"TATCHA\",\"CLINIQUE\",\"OLEHENRIKSEN\",\"SUNDAY RILEY\",\"GLOW RECIPE\",\"HERBIVORE\",\"SUNDAY RILEY\",\"CAUDALIE\",\"CAUDALIE\",\"YOUTH TO THE PEOPLE\",\"LA MER\",\"BIOSSANCE\",\"TARTE\",\"ORIGINS\",\"SON & PARK\",\"TATCHA\",\"OLEHENRIKSEN\",\"AMOREPACIFIC\",\"BOBBI BROWN\",\"DR. JART+\",\"CAUDALIE\",\"HERBIVORE\",\"ERBORIAN\",\"SUNDAY RILEY\",\"IT COSMETICS\",\"TATCHA\",\"GLAMGLOW\",\"DR. JART+\",\"LANEIGE\",\"AMOREPACIFIC\",\"GLAMGLOW\",\"FARMACY\",\"PETER THOMAS ROTH\",\"AMOREPACIFIC\",\"IT COSMETICS\",\"FRESH\",\"FIRST AID BEAUTY\",\"IT COSMETICS\",\"CLINIQUE\",\"JOSIE MARAN\",\"LANEIGE\",\"GLOW RECIPE\",\"LA MER\",\"PHILOSOPHY\",\"PERRICONE MD\",\"LANC\\u00d4ME\",\"ALGENIST\",\"DR. JART+\",\"CLINIQUE\",\"JOSIE MARAN\",\"FARMACY\",\"OLEHENRIKSEN\",\"BELIF\",\"YOUTH TO THE PEOPLE\",\"TATCHA\",\"BIOSSANCE\",\"KIEHL'S SINCE 1851\",\"BOBBI BROWN\",\"FRESH\",\"KORRES\",\"BELIF\",\"YOUTH TO THE PEOPLE\",\"IT COSMETICS\",\"TARTE\",\"SMASHBOX\",\"FIRST AID BEAUTY\",\"MURAD\",\"TATCHA\",\"GUERLAIN\",\"BIOSSANCE\",\"SMASHBOX\",\"ORIGINS\",\"KORA ORGANICS\",\"LANEIGE\",\"SK-II\",\"DR. DENNIS GROSS SKINCARE\",\"REN CLEAN SKINCARE\",\"MURAD\",\"OLEHENRIKSEN\",\"DIOR\",\"KIEHL'S SINCE 1851\",\"FIRST AID BEAUTY\",\"ORIGINS\",\"BIOSSANCE\",\"FIRST AID BEAUTY\",\"MURAD\",\"GUERLAIN\",\"ORIGINS\",\"LA MER\",\"CLINIQUE\",\"CAUDALIE\",\"MURAD\",\"KOPARI\",\"SHISEIDO\",\"CLARINS\",\"SHISEIDO\",\"LA MER\",\"LANCER\",\"TATCHA\",\"DR. DENNIS GROSS SKINCARE\",\"SMASHBOX\",\"KATE SOMERVILLE\",\"SUPERGOOP!\",\"ORIGINS\",\"CAUDALIE\",\"CAUDALIE\",\"SHISEIDO\",\"KORRES\",\"CLINIQUE\",\"SATURDAY SKIN\",\"JOSIE MARAN\",\"MILK MAKEUP\",\"EST\\u00c9E LAUDER\",\"OLEHENRIKSEN\",\"CLINIQUE\",\"PHILOSOPHY\",\"KIEHL'S SINCE 1851\",\"SK-II\",\"CAUDALIE\",\"CLARINS\",\"AMOREPACIFIC\",\"ERBORIAN\",\"DR. JART+\",\"PHILOSOPHY\",\"JOSIE MARAN\",\"KIEHL'S SINCE 1851\",\"FARMACY\",\"PHILOSOPHY\",\"AMOREPACIFIC\",\"SHISEIDO\",\"CAUDALIE\",\"KATE SOMERVILLE\",\"DR. DENNIS GROSS SKINCARE\",\"EST\\u00c9E LAUDER\",\"MURAD\",\"GUERLAIN\",\"AMOREPACIFIC\",\"EST\\u00c9E LAUDER\",\"FIRST AID BEAUTY\",\"DR. BRANDT SKINCARE\",\"LANEIGE\",\"SHISEIDO\",\"GLAMGLOW\",\"LANC\\u00d4ME\",\"BIOSSANCE\",\"ORIGINS\",\"LANC\\u00d4ME\",\"KATE SOMERVILLE\",\"J.ONE\",\"KATE SOMERVILLE\",\"PERRICONE MD\",\"JOSIE MARAN\",\"DR. JART+\",\"HERBIVORE\",\"LANC\\u00d4ME\",\"CAUDALIE\",\"JACK BLACK\",\"LANC\\u00d4ME\",\"FIRST AID BEAUTY\",\"HERBIVORE\",\"TATA HARPER\",\"CLINIQUE\",\"PHILOSOPHY\",\"KIEHL'S SINCE 1851\",\"DIOR\",\"GLAMGLOW\",\"ALGENIST\",\"SHISEIDO\",\"SATURDAY SKIN\",\"HERBIVORE\",\"KATE SOMERVILLE\",\"GO-TO\"],\"name\":[\"Cr\\u00e8me de la Mer\",\"Facial Treatment Essence\",\"Protini\\u2122 Polypeptide Cream\",\"The Moisturizing Soft Cream\",\"Your Skin But Better\\u2122 CC+\\u2122 Cream with SPF 50+\",\"The Water Cream\",\"Lala Retro\\u2122 Whipped Cream\",\"Virgin Marula Luxury Facial Oil\",\"Ultra Facial Cream\",\"Midnight Recovery Concentrate\",\"The True Cream Aqua Bomb\",\"Luna Sleeping Night Oil\",\"Honeymoon Glow AHA Resurfacing Night Serum with Echinacea GreenEnvy\\u2122\",\"The Littles\\u2122\",\"Ultra Repair\\u00ae Cream Intense Hydration\",\"Moisture Surge 72-Hour Auto-Replenishing Hydrator\",\"Cr\\u00e8me de la Mer Mini\",\"Black Tea Firming Overnight Mask\",\"Virgin Marula Luxury Facial Oil Mini\",\"Dr. Andrew Weil For Origins\\u2122 Mega-Mushroom Relief & Resilience Soothing Treatment Lotion\",\"Dramatically Different Moisturizing Lotion+\",\"GenOptics Aura Essence Serum\",\"Pure One Step Camellia Cleansing Oil\",\"Sheer Transformation\\u00ae Perfecting Moisturizer\",\"Your Skin But Better CC+ Cream Oil-Free Matte with SPF 40\",\"Water Sleeping Mask\",\"Facial Treatment Essence Mini\",\"Luminous Dewy Skin Mist\",\"Dramatically Different Moisturizing Gel\",\"Goodnight Glow Retin-ALT Sleeping Cr\\u00e8me\",\"U.F.O. Ultra-Clarifying Face Oil\",\"Watermelon Glow Sleeping Mask\",\"Pink Cloud Rosewater Moisture Cr\\u00e8me\",\"Tidal Brightening Enzyme Water Cream\",\"Beauty Elixir\",\"Grape Water\",\"Adaptogen Deep Moisture Cream\",\"The Renewal Oil\",\"Squalane + Vitamin C Rose Oil\",\"BB Tinted Treatment 12-Hour Primer Broad Spectrum SPF 30 Sunscreen\",\"GinZing\\u2122 Energy-Boosting Gel Moisturizer\",\"Beauty Water\",\"The Silk Cream\",\"C-Rush\\u2122 Brightening Gel Cr\\u00e8me\",\"Color Control Cushion Compact Broad Spectrum SPF 50+\",\"Vitamin Enriched Face Base Priming Moisturizer\",\"Cicapair \\u2122 Tiger Grass Color Correcting Treatment SPF 30\",\"Vinosource Moisturizing Sorbet\",\"Lapis Oil Balancing Facial Oil\",\"CC Cr\\u00e8me High Definition Radiance Face Cream Skin Perfector\",\"C.E.O. C + E antiOXIDANT Protect + Repair Moisturizer\",\"Your Skin But Better\\u2122 CC+Illumination\\u2122 Cream with SPF 50+\",\"Luminous Dewy Skin Night Concentrate\",\"GLOWSTARTER\\u2122 Mega Illuminating Moisturizer\",\"Ceramidin\\u2122 Cream\",\"BB Cushion Hydra Radiance SPF 50\",\"FUTURE RESPONSE Age Defense Creme\",\"THIRSTYMUD\\u2122 Hydrating Treatment\",\"Honey Drop Lightweight Moisturizer with Echinacea GreenEnvy\\u2122\",\"Water Drench Hyaluronic Cloud Cream\",\"Vintage Single Extract Essence\",\"Confidence in a Cream\\u2122 Transforming Moisturizing Super Cream\",\"Cr\\u00e8me Ancienne\\u00ae\",\"Hello FAB Coconut Skin Smoothie Priming Moisturizer\",\"Secret Sauce Clinically Advanced Miraculous Anti-Aging Moisturizer\",\"Moisture Surge Intense Skin Fortifying Hydrator\",\"Argan Daily Moisturizer SPF 47\",\"Water Bank Essence\",\"Watermelon Pink Juice Moisturizer\",\"The Moisturizing Cool Gel Cream\",\"Renewed Hope in A Jar Refreshing & Refining Moisturizer\",\"Cold Plasma Sub-D Firming Neck Treatment\",\"R\\u00e9nergie Lift Multi-Action Night\",\"GENIUS Ultimate Anti-Aging Cream\",\"Cicapair\\u2122 Tiger Grass Cream\",\"Dramatically Different Hydrating Jelly\",\"Whipped Argan Oil Face Butter\",\"Sleep Tight Firming Night Balm with Echinacea GreenEnvy\\u2122\",\"Invigorating Night Transformation\\u2122 Gel\",\"Aqua Bomb Sleeping Mask\",\"Superfood Air-Whip Hyaluronic Acid Moisture Cream\",\"The Indigo Cream Soothing Skin Protectant\",\"Squalane + Omega Repair Cream\",\"Daily Reviving Concentrate\",\"BB Cream SPF 35\",\"Vitamin C Glow Moisturizer\",\"Wild Rose + Vitamin C Advanced Brightening Sleeping Facial\",\"Peat Miracle Revital Cream\",\"Superberry Hydrate + Glow Oil\",\"Miracle Water 3-in-1 Micellar Cleanser\",\"Drink of H2O Hydrating Boost Moisturizer Rainforest of the Sea\\u2122\",\"Photo Finish Primer Water\",\"Ultra Repair Hydra-Firm Sleeping Cream\",\"Retinol Youth Renewal Night Cream\",\"The Essence Plumping Skin Softener\",\"Midnight Secret Late Night Recovery Treatment\",\"Squalane + Probiotic Gel Moisturizer\",\"Photo Finish Primerizer\",\"A Perfect World\\u2122 SPF 40 Age-Defense Moisturizer with White Tea\",\"Noni Glow Face Oil\",\"Water Bank Hydrating Gel\",\"GenOptics Spot Essence Serum\",\"Ferulic + Retinol Anti-Aging Moisturizer\",\"Evercalm\\u2122 Overnight Recovery Balm\",\"Oil-Control Mattifier SPF 15 PA++\",\"Counter Balance\\u2122 Oil Control Hydrator\",\"Capture Totale Dreamskin Advanced\",\"Ultra Facial Oil-Free Gel-Cream\",\"Ultra Repair\\u00ae Hydrating Serum\",\"Dr. Andrew Weil For Origins\\u2122 Mega-Bright SPF 30 Skin Tone Correcting Oil-Free Moisturizer\",\"100% Squalane Oil\",\"Ultra Repair Face Moisturizer\",\"Skin Perfecting Lotion - Blemish Prone/Oily Skin\",\"Abeille Royale Youth Watery Oil\",\"Plantscription\\u2122 SPF 25 Power Anti-Aging Cream\",\"The Moisturizing Soft Lotion\",\"Acne Solutions BB Cream Broad Spectrum SPF 40\",\"Resveratrol Lift Night Infusion Cream\",\"Essential-C Day Moisture Broad Spectrum SPF 30 PA+++\",\"Coconut Melt\",\"Future Solution LX Total Regenerating Cream\",\"Multi-Active Night Cream - Normal to Combination Skin\",\"Ibuki Refining Moisturizer Enriched\",\"The Renewal Oil Mini\",\"The Method: Nourish\",\"Gold Camellia Beauty Oil\",\"Ferulic + Retinol Wrinkle Recovery Overnight Serum\",\"Camera Ready CC Cream Broad Spectrum SPF 30 Dark Spot Correcting\",\"Oil Free Moisturizer\",\"CC Cream Daily Correct Broad Spectrum SPF 35+ Sunscreen\",\"GinZing\\u2122 SPF 40 Energy-Boosting Tinted Moisturizer\",\"Premier Cru Cream\",\"Resveratrol Lift Face Lifting Soft Cream\",\"White Lucent MultiBright Night Cream\",\"Wild Rose Vitamin C Active Brightening Oil\",\"Limited Edition Dramatically Different\\u2122 Moisturizing Gel\",\"Waterfall Glacier Water Cream\",\"100 percent Pure Argan Oil Light\",\"Cooling Water\",\"Resilience Lift Night Lifting/Firming Face and Neck Creme\",\"Truth Revealed\\u2122 Brightening Broad Spectrum SPF 15 Moisturizer\",\"Moisture Surge Hydrating Supercharged Concentrate\",\"Miraculous Anti-wrinkle Miracle Worker\",\"Ultra Facial Overnight Hydrating Mask\",\"Cellumination Cream EX\",\"Vine[activ] Overnight Detox Oil\",\"Extra-Firming Neck Anti-Wrinkle Rejuvenating Cream\",\"MOISTURE BOUND Rejuvenating Cr\\u00e8me\",\"BB Cr\\u00e8me au Ginseng\",\"Black Label Detox BB Beauty Balm\",\"Renewed Hope in A Jar Overnight Recharging & Refining Moisturizer\",\"Argan Cleansing Oil\",\"Ultra Facial Moisturizer\",\"Honey Savior All-in-One Skin Repair Salve with Echinacea GreenEnvy\\u2122 Honey\",\"Ultimate Miracle Worker Multi-Rejuvenating Cream SPF 30\",\"Treatment Toner\",\"Bio-Performance LiftDynamic Cream\",\"Vinosource Intense Moisture Rescue Cream\",\"Wrinkle Warrior\\u2122 2-in-1 Plumping Moisturizer + Serum\",\"Hyaluronic Marine Oil-Free Moisture Cushion\",\"Advanced Night Repair Intensive Recovery Ampoules\",\"Nutrient-Charged Water Gel\",\"Lingerie de Peau BB Cream\",\"MOISTURE BOUND Refreshing Hydra-Gel Oil-Free\",\"Revitalizing Supreme+ Global Anti-Aging Cell Power Creme SPF 15\",\"Hello FAB Coconut Water Cream\",\"Do Not Age with Dr. Brandt Moisturizing Neck Cream\",\"Water Bank Dual Layer Face Oil\",\"Ibuki Beauty Sleeping Mask\",\"VOLCASMIC\\u2122 Matte Glow Moisturizer\",\"R\\u00e9nergie Lift Multi-Action Sunscreen Broad Spectrum SPF 15 For All Skin Types\",\"Squalane + Antioxidant Cleansing Oil\",\"Plantscription\\u2122 Youth-Renewing Power Night Cream\",\"\\u00c9nergie de Vie The Smoothing & Plumping Water-Infused Cream\",\"ExfoliKate Glow Moisturizer\",\"Jelly Pack\",\"RetAsphere\\u2122 2-in-1 Retinol Night Cream\",\"High Potency Classics: Face Finishing & Firming Moisturizer\",\"Argan Infinity Cream Intensive Creamy Oil\",\"Water Drop Hydrating Moisturizer\",\"Moon Fruit Superfruit Night Treatment\",\"Visionnaire Advanced Multi-Correcting Cream\",\"Premier Cru Rich Cream\",\"Clean Break\\u2122 Oil-Free Moisturizer\",\"Hydra Zen Anti-Stress Gel Moisturizer\",\"5 in 1 Face Cream SPF 30\",\"Rose Hibiscus Coconut Water Hydrating Face Mist\",\"Retinoic Nutrient Face Oil\",\"Limited Edition Dramatically Different Moisturizing Lotion+\\u2122\",\"Hope In A Jar\",\"Ultra Facial Cream SPF 30\",\"Capture Youth Age-Delay Advanced Cr\\u00e8me\",\"DREAMDUO\\u2122 Overnight Transforming Treatment\",\"POWER Recharging Night Pressed Serum\",\"White Lucent All Day Brightener Broad Spectrum SPF 23 Moisturizer\",\"Featherweight Daily Moisturizing Cream\",\"Jasmine Green Tea Balancing Toner\",\"Goat Milk Moisturizing Cream\",\"Face Hero\"],\"price\":[175,179,68,175,38,68,60,72,29,47,38,105,58,90,30,39,85,92,40,34,28,240,48,38,38,25,99,48,28,55,80,45,48,65,18,10,58,245,72,37,29,30,120,44,60,60,52,39,72,44,65,38,110,49,48,38,195,59,45,52,145,48,290,28,68,39,32,36,39,175,39,135,122,112,48,28,40,48,48,34,48,85,58,47,47,42,48,58,44,38,39,32,38,82,95,29,52,42,45,68,35,225,75,48,40,34,115,29,36,56,58,24,40,50,60,270,39,76,63,28,280,57,45,130,125,95,88,42,65,34,39,140,76,92,54,39,39,48,24,92,55,39,60,35,160,50,90,150,39,36,49,32,29,34,75,50,115,39,95,60,115,60,54,100,82,34,67,38,40,49,99,30,60,55,65,42,85,69,28,36,58,88,140,30,48,40,32,48,39,39,29,95,59,95,62,49,39,65,34],\"rank\":{\"__ndarray__\":\"ZmZmZmZmEEBmZmZmZmYQQJqZmZmZmRFAZmZmZmZmDkBmZmZmZmYQQM3MzMzMzBBAzczMzMzMEECamZmZmZkRQJqZmZmZmRFAmpmZmZmZEUAAAAAAAAASQGZmZmZmZhBAZmZmZmZmEkCamZmZmZkRQGZmZmZmZhJAmpmZmZmZEUBmZmZmZmYQQGZmZmZmZhBAAAAAAAAAEkCamZmZmZkRQDMzMzMzMw9AZmZmZmZmEEAAAAAAAAASQM3MzMzMzBBAMzMzMzMzD0CamZmZmZkRQGZmZmZmZhBAAAAAAAAAEEAAAAAAAAASQDMzMzMzMxFAzczMzMzMEEBmZmZmZmYQQGZmZmZmZhJAzczMzMzMEEAzMzMzMzMRQAAAAAAAABJAMzMzMzMzE0DNzMzMzMwQQJqZmZmZmRFAMzMzMzMzEUCamZmZmZkRQGZmZmZmZhJAMzMzMzMzEUBmZmZmZmYSQAAAAAAAABBAAAAAAAAAEkDNzMzMzMwQQM3MzMzMzBBAmpmZmZmZEUBmZmZmZmYQQGZmZmZmZhBAMzMzMzMzD0AAAAAAAAASQAAAAAAAABBAZmZmZmZmEkAzMzMzMzMRQJqZmZmZmRFAMzMzMzMzEUBmZmZmZmYQQJqZmZmZmRFAAAAAAAAAEkAzMzMzMzMRQJqZmZmZmRFAzczMzMzMEEBmZmZmZmYOQJqZmZmZmRFAMzMzMzMzD0DNzMzMzMwSQGZmZmZmZhBAZmZmZmZmCkBmZmZmZmYQQGZmZmZmZg5AMzMzMzMzEUAzMzMzMzMRQDMzMzMzMxFAAAAAAAAAEEBmZmZmZmYQQDMzMzMzMxFAMzMzMzMzEUDNzMzMzMwSQM3MzMzMzBBAzczMzMzMEEDNzMzMzMwSQM3MzMzMzBJAMzMzMzMzD0AzMzMzMzMRQJqZmZmZmRFAzczMzMzMEkAzMzMzMzMTQAAAAAAAABJAmpmZmZmZEUBmZmZmZmYQQDMzMzMzMxFAZmZmZmZmEkCamZmZmZkRQM3MzMzMzBBAmpmZmZmZEUBmZmZmZmYQQAAAAAAAAAxAMzMzMzMzEUBmZmZmZmYSQM3MzMzMzBBAZmZmZmZmEECamZmZmZkTQM3MzMzMzBBAMzMzMzMzEUAzMzMzMzMRQGZmZmZmZhBAzczMzMzMEEBmZmZmZmYQQGZmZmZmZhJAzczMzMzMEEAzMzMzMzMRQGZmZmZmZhBAzczMzMzMEkDNzMzMzMwMQGZmZmZmZhBAZmZmZmZmEEAAAAAAAAAQQM3MzMzMzBBAAAAAAAAAEEDNzMzMzMwQQJqZmZmZmRFAAAAAAAAAEEAAAAAAAAASQGZmZmZmZhJAmpmZmZmZEUAzMzMzMzMPQM3MzMzMzBBAmpmZmZmZEUAAAAAAAAAQQJqZmZmZmRFAMzMzMzMzD0BmZmZmZmYQQJqZmZmZmRFAAAAAAAAAFEBmZmZmZmYSQJqZmZmZmRFAZmZmZmZmDkDNzMzMzMwQQM3MzMzMzBBAAAAAAAAAEEAzMzMzMzMRQJqZmZmZmRFAMzMzMzMzEUAAAAAAAAASQGZmZmZmZhBAZmZmZmZmEECamZmZmZkRQGZmZmZmZhBAmpmZmZmZEUBmZmZmZmYQQDMzMzMzMw9AAAAAAAAAEkAAAAAAAAAQQJqZmZmZmRFAmpmZmZmZDUDNzMzMzMwQQDMzMzMzMw9AzczMzMzMEECamZmZmZkTQGZmZmZmZhJAMzMzMzMzEUDNzMzMzMwQQAAAAAAAABBAzczMzMzMEEDNzMzMzMwQQAAAAAAAABRAmpmZmZmZEUBmZmZmZmYQQDMzMzMzMw9AmpmZmZmZEUCamZmZmZkRQM3MzMzMzBBAZmZmZmZmEkAzMzMzMzMRQDMzMzMzMxFAMzMzMzMzEUAAAAAAAAASQGZmZmZmZhBAAAAAAAAAEEBmZmZmZmYSQAAAAAAAABJAAAAAAAAAEkBmZmZmZmYSQDMzMzMzMw9AZmZmZmZmEEAzMzMzMzMRQAAAAAAAAAAAAAAAAAAAEEDNzMzMzMwQQDMzMzMzMw9AzczMzMzMEECamZmZmZkRQGZmZmZmZhJAZmZmZmZmEkDNzMzMzMwQQGZmZmZmZhBAMzMzMzMzE0A=\",\"dtype\":\"float64\",\"shape\":[199]}},\"selected\":{\"id\":\"58d9eb20-c3ba-49d2-afd3-dd77a10909a8\",\"type\":\"Selection\"},\"selection_policy\":{\"id\":\"84d64de3-7f3b-4f94-baf4-03d3426375c9\",\"type\":\"UnionRenderers\"}},\"id\":\"eadabff2-21c5-4836-8f20-8336a61f30a2\",\"type\":\"ColumnDataSource\"},{\"attributes\":{},\"id\":\"58d9eb20-c3ba-49d2-afd3-dd77a10909a8\",\"type\":\"Selection\"},{\"attributes\":{\"callback\":null},\"id\":\"7e88bd77-9bb7-461f-9cab-dc5f78160cb6\",\"type\":\"DataRange1d\"},{\"attributes\":{},\"id\":\"e35745ad-b9ab-46da-b2c0-7095ddf4b589\",\"type\":\"PanTool\"},{\"attributes\":{},\"id\":\"c8a9dc54-f973-4e8e-a1e4-cb68814be8e9\",\"type\":\"LinearScale\"},{\"attributes\":{\"fill_alpha\":{\"value\":0.8},\"fill_color\":{\"value\":\"#FF7373\"},\"line_alpha\":{\"value\":0.8},\"line_color\":{\"value\":\"#FF7373\"},\"size\":{\"units\":\"screen\",\"value\":10},\"x\":{\"field\":\"X\"},\"y\":{\"field\":\"Y\"}},\"id\":\"99a6c174-1e1e-4f3f-b83f-297f7141bc3e\",\"type\":\"Circle\"},{\"attributes\":{},\"id\":\"c6e9a1bf-7f2d-4901-a8ea-375e99f315f3\",\"type\":\"BasicTickFormatter\"},{\"attributes\":{\"axis_label\":\"T-SNE 1\",\"formatter\":{\"id\":\"c6e9a1bf-7f2d-4901-a8ea-375e99f315f3\",\"type\":\"BasicTickFormatter\"},\"plot\":{\"id\":\"ebd7d08b-1913-4f65-9f34-2b3d52785256\",\"subtype\":\"Figure\",\"type\":\"Plot\"},\"ticker\":{\"id\":\"d950ea52-527f-482f-8ce2-30490b198db7\",\"type\":\"BasicTicker\"}},\"id\":\"1a4ac4a5-a2a3-477e-8a07-265c5b2a5a31\",\"type\":\"LinearAxis\"},{\"attributes\":{\"background_fill_alpha\":{\"value\":0.2},\"background_fill_color\":{\"value\":\"beige\"},\"below\":[{\"id\":\"1a4ac4a5-a2a3-477e-8a07-265c5b2a5a31\",\"type\":\"LinearAxis\"}],\"left\":[{\"id\":\"4a826e36-5aa4-435b-9f36-5cf0af329bc1\",\"type\":\"LinearAxis\"}],\"plot_height\":400,\"plot_width\":500,\"renderers\":[{\"id\":\"1a4ac4a5-a2a3-477e-8a07-265c5b2a5a31\",\"type\":\"LinearAxis\"},{\"id\":\"3f052484-3737-48c7-818e-c5185c6248c5\",\"type\":\"Grid\"},{\"id\":\"4a826e36-5aa4-435b-9f36-5cf0af329bc1\",\"type\":\"LinearAxis\"},{\"id\":\"ea06b922-898f-4238-9a23-fdbbbb1d4a14\",\"type\":\"Grid\"},{\"id\":\"5fd84317-1d33-4123-b5b0-4f1ca93289e6\",\"type\":\"BoxAnnotation\"},{\"id\":\"ef981548-f9e6-4504-bbd9-100b9db97c4c\",\"type\":\"GlyphRenderer\"}],\"title\":{\"id\":\"23c3258c-6e2e-49d1-bc81-ba5513699498\",\"type\":\"Title\"},\"toolbar\":{\"id\":\"4a694f1b-54d3-4eae-b4b2-df8fbe42b728\",\"type\":\"Toolbar\"},\"x_range\":{\"id\":\"5a049af5-96bc-480a-b7c9-684e23ffc302\",\"type\":\"DataRange1d\"},\"x_scale\":{\"id\":\"c8a9dc54-f973-4e8e-a1e4-cb68814be8e9\",\"type\":\"LinearScale\"},\"y_range\":{\"id\":\"7e88bd77-9bb7-461f-9cab-dc5f78160cb6\",\"type\":\"DataRange1d\"},\"y_scale\":{\"id\":\"050952e4-4d41-4b9c-a8cf-1c02207a3655\",\"type\":\"LinearScale\"}},\"id\":\"ebd7d08b-1913-4f65-9f34-2b3d52785256\",\"subtype\":\"Figure\",\"type\":\"Plot\"},{\"attributes\":{},\"id\":\"84d64de3-7f3b-4f94-baf4-03d3426375c9\",\"type\":\"UnionRenderers\"},{\"attributes\":{},\"id\":\"65a56917-86c4-4304-a1cb-2f325cfa8ed6\",\"type\":\"ResetTool\"},{\"attributes\":{\"plot\":{\"id\":\"ebd7d08b-1913-4f65-9f34-2b3d52785256\",\"subtype\":\"Figure\",\"type\":\"Plot\"},\"ticker\":{\"id\":\"d950ea52-527f-482f-8ce2-30490b198db7\",\"type\":\"BasicTicker\"}},\"id\":\"3f052484-3737-48c7-818e-c5185c6248c5\",\"type\":\"Grid\"},{\"attributes\":{},\"id\":\"af180241-1578-432d-88a7-26ab02e28aa8\",\"type\":\"BasicTickFormatter\"},{\"attributes\":{\"data_source\":{\"id\":\"eadabff2-21c5-4836-8f20-8336a61f30a2\",\"type\":\"ColumnDataSource\"},\"glyph\":{\"id\":\"99a6c174-1e1e-4f3f-b83f-297f7141bc3e\",\"type\":\"Circle\"},\"hover_glyph\":null,\"muted_glyph\":null,\"nonselection_glyph\":{\"id\":\"77f9f060-adc9-402c-b897-47843f998afe\",\"type\":\"Circle\"},\"selection_glyph\":null,\"view\":{\"id\":\"e1e561c7-6c21-48ec-8cd5-4d7da351064b\",\"type\":\"CDSView\"}},\"id\":\"ef981548-f9e6-4504-bbd9-100b9db97c4c\",\"type\":\"GlyphRenderer\"},{\"attributes\":{},\"id\":\"d950ea52-527f-482f-8ce2-30490b198db7\",\"type\":\"BasicTicker\"},{\"attributes\":{\"callback\":null},\"id\":\"5a049af5-96bc-480a-b7c9-684e23ffc302\",\"type\":\"DataRange1d\"},{\"attributes\":{\"active_drag\":\"auto\",\"active_inspect\":\"auto\",\"active_multi\":null,\"active_scroll\":\"auto\",\"active_tap\":\"auto\",\"tools\":[{\"id\":\"e35745ad-b9ab-46da-b2c0-7095ddf4b589\",\"type\":\"PanTool\"},{\"id\":\"7a253c00-9ec0-49c2-a819-1a772416cb5b\",\"type\":\"WheelZoomTool\"},{\"id\":\"218011d5-2359-4aea-a098-610ec8da1a74\",\"type\":\"BoxZoomTool\"},{\"id\":\"07be9b77-a2f7-405e-8eb9-11d2b77ef68c\",\"type\":\"SaveTool\"},{\"id\":\"65a56917-86c4-4304-a1cb-2f325cfa8ed6\",\"type\":\"ResetTool\"},{\"id\":\"a8e5a12e-a88c-43c7-9672-1de53e9fc519\",\"type\":\"HelpTool\"},{\"id\":\"f06d5197-a3fa-4a75-b659-5dc31c3d7f6e\",\"type\":\"HoverTool\"}]},\"id\":\"4a694f1b-54d3-4eae-b4b2-df8fbe42b728\",\"type\":\"Toolbar\"},{\"attributes\":{},\"id\":\"07be9b77-a2f7-405e-8eb9-11d2b77ef68c\",\"type\":\"SaveTool\"},{\"attributes\":{\"overlay\":{\"id\":\"5fd84317-1d33-4123-b5b0-4f1ca93289e6\",\"type\":\"BoxAnnotation\"}},\"id\":\"218011d5-2359-4aea-a098-610ec8da1a74\",\"type\":\"BoxZoomTool\"},{\"attributes\":{},\"id\":\"a8e5a12e-a88c-43c7-9672-1de53e9fc519\",\"type\":\"HelpTool\"},{\"attributes\":{\"axis_label\":\"T-SNE 2\",\"formatter\":{\"id\":\"af180241-1578-432d-88a7-26ab02e28aa8\",\"type\":\"BasicTickFormatter\"},\"plot\":{\"id\":\"ebd7d08b-1913-4f65-9f34-2b3d52785256\",\"subtype\":\"Figure\",\"type\":\"Plot\"},\"ticker\":{\"id\":\"8254987c-8219-444c-b0b3-67d2f1bb45a5\",\"type\":\"BasicTicker\"}},\"id\":\"4a826e36-5aa4-435b-9f36-5cf0af329bc1\",\"type\":\"LinearAxis\"},{\"attributes\":{\"source\":{\"id\":\"eadabff2-21c5-4836-8f20-8336a61f30a2\",\"type\":\"ColumnDataSource\"}},\"id\":\"e1e561c7-6c21-48ec-8cd5-4d7da351064b\",\"type\":\"CDSView\"},{\"attributes\":{\"dimension\":1,\"plot\":{\"id\":\"ebd7d08b-1913-4f65-9f34-2b3d52785256\",\"subtype\":\"Figure\",\"type\":\"Plot\"},\"ticker\":{\"id\":\"8254987c-8219-444c-b0b3-67d2f1bb45a5\",\"type\":\"BasicTicker\"}},\"id\":\"ea06b922-898f-4238-9a23-fdbbbb1d4a14\",\"type\":\"Grid\"},{\"attributes\":{\"plot\":null,\"text\":\"\"},\"id\":\"23c3258c-6e2e-49d1-bc81-ba5513699498\",\"type\":\"Title\"},{\"attributes\":{\"bottom_units\":\"screen\",\"fill_alpha\":{\"value\":0.5},\"fill_color\":{\"value\":\"lightgrey\"},\"left_units\":\"screen\",\"level\":\"overlay\",\"line_alpha\":{\"value\":1.0},\"line_color\":{\"value\":\"black\"},\"line_dash\":[4,4],\"line_width\":{\"value\":2},\"plot\":null,\"render_mode\":\"css\",\"right_units\":\"screen\",\"top_units\":\"screen\"},\"id\":\"5fd84317-1d33-4123-b5b0-4f1ca93289e6\",\"type\":\"BoxAnnotation\"}],\"root_ids\":[\"ebd7d08b-1913-4f65-9f34-2b3d52785256\"]},\"title\":\"Bokeh Application\",\"version\":\"0.13.0\"}};\n", 1062 | " var render_items = [{\"docid\":\"1e2b04ec-9534-4f15-b7ae-31aa723bf253\",\"notebook_comms_target\":\"43694705-6a99-41df-a870-80ff6d5a508c\",\"roots\":{\"ebd7d08b-1913-4f65-9f34-2b3d52785256\":\"ddfb11e2-78bf-4fb7-b9cd-7b79edefe882\"}}];\n", 1063 | " root.Bokeh.embed.embed_items_notebook(docs_json, render_items);\n", 1064 | "\n", 1065 | " }\n", 1066 | " if (root.Bokeh !== undefined) {\n", 1067 | " embed_document(root);\n", 1068 | " } else {\n", 1069 | " var attempts = 0;\n", 1070 | " var timer = setInterval(function(root) {\n", 1071 | " if (root.Bokeh !== undefined) {\n", 1072 | " embed_document(root);\n", 1073 | " clearInterval(timer);\n", 1074 | " }\n", 1075 | " attempts++;\n", 1076 | " if (attempts > 100) {\n", 1077 | " console.log(\"Bokeh: ERROR: Unable to run BokehJS code because BokehJS library is missing\")\n", 1078 | " clearInterval(timer);\n", 1079 | " }\n", 1080 | " }, 10, root)\n", 1081 | " }\n", 1082 | "})(window);" 1083 | ], 1084 | "application/vnd.bokehjs_exec.v0+json": "" 1085 | }, 1086 | "metadata": { 1087 | "application/vnd.bokehjs_exec.v0+json": { 1088 | "id": "ebd7d08b-1913-4f65-9f34-2b3d52785256" 1089 | } 1090 | }, 1091 | "output_type": "display_data" 1092 | }, 1093 | { 1094 | "data": { 1095 | "text/html": [ 1096 | "

<Bokeh Notebook handle for In[31]>

" 1097 | ], 1098 | "text/plain": [ 1099 | "" 1100 | ] 1101 | }, 1102 | "execution_count": 31, 1103 | "metadata": {}, 1104 | "output_type": "execute_result" 1105 | } 1106 | ], 1107 | "source": [ 1108 | "# interact the plot with callback \n", 1109 | "output_notebook()\n", 1110 | "\n", 1111 | "interact(update, op1 = option_1, op2 = option_2)\n", 1112 | "show(plot, notebook_handle = True)" 1113 | ] 1114 | }, 1115 | { 1116 | "cell_type": "markdown", 1117 | "metadata": { 1118 | "colab_type": "text", 1119 | "id": "fr49DdTASspX" 1120 | }, 1121 | "source": [ 1122 | "# 3. Cosine similarity" 1123 | ] 1124 | }, 1125 | { 1126 | "cell_type": "markdown", 1127 | "metadata": { 1128 | "colab_type": "text", 1129 | "id": "mSRxcjv7SspY" 1130 | }, 1131 | "source": [ 1132 | "Now each item is plotted on the plane we can simply calculate the cosine similarities between each point. I took [Peat Miracle Revital Cream](https://www.sephora.com/product/peat-miracle-revital-cream-P412440) from Belif as an example. " 1133 | ] 1134 | }, 1135 | { 1136 | "cell_type": "code", 1137 | "execution_count": 11, 1138 | "metadata": { 1139 | "colab": { 1140 | "base_uri": "https://localhost:8080/", 1141 | "height": 77 1142 | }, 1143 | "colab_type": "code", 1144 | "id": "8Lio8LDnSspZ", 1145 | "outputId": "25aea4e3-95db-4484-c0bc-7a4bffe11605" 1146 | }, 1147 | "outputs": [ 1148 | { 1149 | "data": { 1150 | "text/html": [ 1151 | "
\n", 1152 | "\n", 1165 | "\n", 1166 | " \n", 1167 | " \n", 1168 | " \n", 1169 | " \n", 1170 | " \n", 1171 | " \n", 1172 | " \n", 1173 | " \n", 1174 | " \n", 1175 | " \n", 1176 | " \n", 1177 | " \n", 1178 | " \n", 1179 | " \n", 1180 | " \n", 1181 | " \n", 1182 | " \n", 1183 | " \n", 1184 | " \n", 1185 | " \n", 1186 | " \n", 1187 | " \n", 1188 | " \n", 1189 | " \n", 1190 | "
LabelbrandnamepricerankSVD1SVD2
87Moisturizer_DryBELIFPeat Miracle Revital Cream584.72.52592-0.017333
\n", 1191 | "
" 1192 | ], 1193 | "text/plain": [ 1194 | " Label brand name price rank SVD1 \\\n", 1195 | "87 Moisturizer_Dry BELIF Peat Miracle Revital Cream 58 4.7 2.52592 \n", 1196 | "\n", 1197 | " SVD2 \n", 1198 | "87 -0.017333 " 1199 | ] 1200 | }, 1201 | "execution_count": 11, 1202 | "metadata": { 1203 | "tags": [] 1204 | }, 1205 | "output_type": "execute_result" 1206 | } 1207 | ], 1208 | "source": [ 1209 | "df_2 = df[df.Label == 'Moisturizer_Dry'].reset_index().drop('index', axis = 1)\n", 1210 | "df_2['dist'] = 0.0\n", 1211 | "\n", 1212 | "myItem = df_2[df_2.name.str.contains('Peat Miracle Revital')]\n", 1213 | "myItem" 1214 | ] 1215 | }, 1216 | { 1217 | "cell_type": "code", 1218 | "execution_count": 17, 1219 | "metadata": { 1220 | "colab": { 1221 | "base_uri": "https://localhost:8080/", 1222 | "height": 34 1223 | }, 1224 | "colab_type": "code", 1225 | "id": "5iDC0uwiSspk", 1226 | "outputId": "64a7b885-f6bd-4e26-dbfe-d8d2ac0695cf" 1227 | }, 1228 | "outputs": [ 1229 | { 1230 | "data": { 1231 | "text/plain": [ 1232 | "array([[ 2.52591969, -0.01733327]])" 1233 | ] 1234 | }, 1235 | "execution_count": 17, 1236 | "metadata": { 1237 | "tags": [] 1238 | }, 1239 | "output_type": "execute_result" 1240 | } 1241 | ], 1242 | "source": [ 1243 | "# getting the array for myItem\n", 1244 | "P1 = np.array([myItem.SVD1.values, myItem.SVD2.values]).reshape(1, -1)\n", 1245 | "P1" 1246 | ] 1247 | }, 1248 | { 1249 | "cell_type": "code", 1250 | "execution_count": 18, 1251 | "metadata": { 1252 | "colab": { 1253 | "base_uri": "https://localhost:8080/", 1254 | "height": 134 1255 | }, 1256 | "colab_type": "code", 1257 | "id": "XtnCd_pH136C", 1258 | "outputId": "8d01bb5a-ea65-412a-b856-3336a702ee65" 1259 | }, 1260 | "outputs": [ 1261 | { 1262 | "name": "stderr", 1263 | "output_type": "stream", 1264 | "text": [ 1265 | "/usr/local/lib/python3.6/dist-packages/ipykernel_launcher.py:3: SettingWithCopyWarning: \n", 1266 | "A value is trying to be set on a copy of a slice from a DataFrame\n", 1267 | "\n", 1268 | "See the caveats in the documentation: http://pandas.pydata.org/pandas-docs/stable/indexing.html#indexing-view-versus-copy\n", 1269 | " This is separate from the ipykernel package so we can avoid doing imports until\n", 1270 | "/usr/local/lib/python3.6/dist-packages/ipykernel_launcher.py:3: RuntimeWarning: invalid value encountered in sqrt\n", 1271 | " This is separate from the ipykernel package so we can avoid doing imports until\n" 1272 | ] 1273 | } 1274 | ], 1275 | "source": [ 1276 | "# cosine similarities with other items\n", 1277 | "for i in range(len(df_2)):\n", 1278 | " P2 = np.array([df_2['X'][i], df_2['Y'][i]]).reshape(-1, 1)\n", 1279 | " df_2.dist[i] = (P1 * P2).sum() / (np.sqrt(np.sum(P1))*np.sqrt(np.sum(P2)))" 1280 | ] 1281 | }, 1282 | { 1283 | "cell_type": "markdown", 1284 | "metadata": { 1285 | "colab_type": "text", 1286 | "id": "4L-3V-iDSspr" 1287 | }, 1288 | "source": [ 1289 | "If we sort the result in ascending order, we can see the top 5 closest cosmetic items like below." 1290 | ] 1291 | }, 1292 | { 1293 | "cell_type": "code", 1294 | "execution_count": 16, 1295 | "metadata": { 1296 | "colab": { 1297 | "base_uri": "https://localhost:8080/", 1298 | "height": 195 1299 | }, 1300 | "colab_type": "code", 1301 | "id": "Ddfjhl1MSsps", 1302 | "outputId": "68613874-7406-40a9-d1ec-2f9be00df669" 1303 | }, 1304 | "outputs": [ 1305 | { 1306 | "data": { 1307 | "text/html": [ 1308 | "
\n", 1309 | "\n", 1322 | "\n", 1323 | " \n", 1324 | " \n", 1325 | " \n", 1326 | " \n", 1327 | " \n", 1328 | " \n", 1329 | " \n", 1330 | " \n", 1331 | " \n", 1332 | " \n", 1333 | " \n", 1334 | " \n", 1335 | " \n", 1336 | " \n", 1337 | " \n", 1338 | " \n", 1339 | " \n", 1340 | " \n", 1341 | " \n", 1342 | " \n", 1343 | " \n", 1344 | " \n", 1345 | " \n", 1346 | " \n", 1347 | " \n", 1348 | " \n", 1349 | " \n", 1350 | " \n", 1351 | " \n", 1352 | " \n", 1353 | " \n", 1354 | " \n", 1355 | " \n", 1356 | " \n", 1357 | " \n", 1358 | " \n", 1359 | " \n", 1360 | " \n", 1361 | " \n", 1362 | " \n", 1363 | "
namebranddist
95Midnight Secret Late Night Recovery TreatmentGUERLAIN4.337068e-08
171Water Drop Hydrating MoisturizerDR. JART+2.001145e-07
114Coconut MeltKOPARI2.168347e-07
109Black Tea Age-Delay CreamFRESH3.303227e-07
108Abeille Royale Youth Watery OilGUERLAIN3.422469e-07
\n", 1364 | "
" 1365 | ], 1366 | "text/plain": [ 1367 | " name brand dist\n", 1368 | "95 Midnight Secret Late Night Recovery Treatment GUERLAIN 4.337068e-08\n", 1369 | "171 Water Drop Hydrating Moisturizer DR. JART+ 2.001145e-07\n", 1370 | "114 Coconut Melt KOPARI 2.168347e-07\n", 1371 | "109 Black Tea Age-Delay Cream FRESH 3.303227e-07\n", 1372 | "108 Abeille Royale Youth Watery Oil GUERLAIN 3.422469e-07" 1373 | ] 1374 | }, 1375 | "execution_count": 16, 1376 | "metadata": { 1377 | "tags": [] 1378 | }, 1379 | "output_type": "execute_result" 1380 | } 1381 | ], 1382 | "source": [ 1383 | "df_2 = df_2.sort_values('dist')\n", 1384 | "df_2[['name', 'brand', 'dist']].head(5)" 1385 | ] 1386 | }, 1387 | { 1388 | "cell_type": "markdown", 1389 | "metadata": { 1390 | "colab_type": "text", 1391 | "id": "h_j84WVDSsp1" 1392 | }, 1393 | "source": [ 1394 | "These are the top 5 cosmetics that have similar properties with myItem. With this list, we can produce a recommendation for new products. If we sort them in descending way, then the list could be used as *'the worst choice for you'*." 1395 | ] 1396 | } 1397 | ], 1398 | "metadata": { 1399 | "colab": { 1400 | "include_colab_link": true, 1401 | "name": "Copy of Copy of cosmtic_map.ipynb", 1402 | "provenance": [], 1403 | "toc_visible": true, 1404 | "version": "0.3.2" 1405 | }, 1406 | "kernelspec": { 1407 | "display_name": "Python 3", 1408 | "language": "python", 1409 | "name": "python3" 1410 | }, 1411 | "language_info": { 1412 | "codemirror_mode": { 1413 | "name": "ipython", 1414 | "version": 3 1415 | }, 1416 | "file_extension": ".py", 1417 | "mimetype": "text/x-python", 1418 | "name": "python", 1419 | "nbconvert_exporter": "python", 1420 | "pygments_lexer": "ipython3", 1421 | "version": "3.7.0" 1422 | } 1423 | }, 1424 | "nbformat": 4, 1425 | "nbformat_minor": 1 1426 | } 1427 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Cosmetic Recommendation System 2 | : *Mapping cosmetic items based on their ingredients similarities and giving content-based filtering recommendation* 3 | 4 |
5 | 6 | ## ***1. For the Skin Beauty: What Can We Do for Ourselves?*** 7 | Whenever I want to try a new cosmetic item, it’s so difficult for me to choose which one will fit for me. It’s actually more than difficult. It’s sometimes scary because new items that I’ve never tried before tend to bring me skin trouble. If you have an experience like me, you could relate to this situation. We know the information we need here would be at the back of the cosmetic bottles. But.. It's really hard to get any clues from these chemistry names if you aren't a chemist. 8 | 9 | ![page](https://github.com/jjone36/Cosmetic/blob/master/image/image.png) 10 | 11 | So instead of just being worried about my new choice, I decided to build a simple cosmetic recommendation on my own. 12 |
13 | 14 | * **Project Date:** Nov, 2018 15 | * **Applied skills:** Web scraping with Selenium. Text mining and word embedding. Natural Language Processing. Dimension reduction with t-SNE. Content-based Recommendation Filtering using Cosine similarities of chemical compositions. Interactive Visualization with Bokeh. 16 | 17 | * **Publication:** "[For Your Skin Beauty: Mapping Cosmetic Items with Bokeh](https://towardsdatascience.com/for-your-skin-beauty-mapping-cosmetic-items-with-bokeh-af7523ca68e5)", Nov 28. 2018, Medium 18 | 19 | 👉 ***Note: This project is selected as an online project tutorial on [DataCamp](https://www.datacamp.com/projects). Stay tuned for the finalized product!*** 20 | 21 |
22 | 23 | ## ***2. File Details*** 24 | - **[01.scraping.py](https://github.com/jjone36/Cosmetic/blob/master/01.scraping.py)** : Web scarping data on Sephora with Selenium 25 | 26 | - **[02.preprocessing.py](https://github.com/jjone36/Cosmetic/blob/master/02.preprocessing.py)** : Data cleaning & preprocessing. 27 | 28 | - **[03.modeling.py](https://github.com/jjone36/Cosmetic/blob/master/03.modeling.py)** : Creating a database for the whole items and modeling with word embedding and dimension reduction technique. 29 | 30 | - **[04.Visualization.ipynb](https://github.com/jjone36/Cosmetic/blob/master/04.Visualization.ipynb)** : Visualizing the items as an interactive bokeh app with Bokeh 31 | 32 | ![map](https://github.com/jjone36/Cosmetic/blob/master/image/map.gif) 33 | 34 |
35 | 36 | ## ***3. What's Next?*** 37 | - Gather more data and make my own database with MySQL. 38 | - Apply advanced models with Neural Networks 39 | - Create a practical Web Page/App using Flask 40 | -------------------------------------------------------------------------------- /image/image.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jjone36/Cosmetic/4098b5baa2f7eff8d1747bcb55e127ec1695202d/image/image.png -------------------------------------------------------------------------------- /image/map.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jjone36/Cosmetic/4098b5baa2f7eff8d1747bcb55e127ec1695202d/image/map.gif --------------------------------------------------------------------------------