{"cells":[{"metadata":{"_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19","trusted":true},"cell_type":"code","source":"# This Python 3 environment comes with many helpful analytics libraries installed\n# It is defined by the kaggle/python docker image: https://github.com/kaggle/docker-python\n# For example, here's several helpful packages to load in \n\nimport numpy as np # linear algebra\nimport pandas as pd # data processing, CSV file I/O (e.g. pd.read_csv)\n\n# Input data files are available in the \"../input/\" directory.\n# For example, running this (by clicking run or pressing Shift+Enter) will list all files under the input directory\n\nimport os\nfor dirname, _, filenames in os.walk('/kaggle/input'):\n    for filename in filenames:\n        print(os.path.join(dirname, filename))\n\n# Any results you write to the current directory are saved as output.","execution_count":null,"outputs":[]},{"metadata":{"_uuid":"d629ff2d2480ee46fbb7e2d37f6b5fab8052498a","_cell_guid":"79c7e3d0-c299-4dcb-8224-4455121ee9b0","trusted":true},"cell_type":"code","source":"import numpy as np\nimport pandas as pd\n\nimport os\nimport matplotlib.pyplot as plt\nimport seaborn as sns\nprint(os.listdir(\"/kaggle/input\"))","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"import matplotlib as mpl\n\nmpl.rcParams['figure.figsize'] = [15, 7]\nmpl.rcParams['figure.dpi'] = 80\nmpl.rcParams['savefig.dpi'] = 100\n\nmpl.rcParams['font.size'] = 14\nmpl.rcParams['legend.fontsize'] = 'large'\nmpl.rcParams['figure.titlesize'] = 'medium'","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"!head -n 5 /kaggle/input/expedia-hotel-recommendations/train.csv","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"!wc -l /kaggle/input/expedia-hotel-recommendations/train.csv","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"data = pd.read_csv(\"/kaggle/input/expedia-hotel-recommendations/train.csv\", nrows=100000)","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"# Goals 목표:\n- Hotel_clusters are dependent on which categorical variable?\n호텔 군집은 어떤 범주형 변수에 의존하는가?\n    - Graph the top 4 도표의 상위 4개\n- Hotel_clusters are dependent on which numerical variable?\n호텔 군집은 어떤 숫자 변수에 의존하는가?\n- How correlated are the user's country to the hotel's country? Continent?\n사용자의 나라와 호텔의 나라, 혹은 대륙은 어떤 상관관계를 갖고 있는가?\n\n# Models to check out 확인해야 할 모델\n- The closest 5 hotels in the latent space clicked by each person should be recommended at the top 5 (Content-based filtering)\n각 사용자가 클릭한 잠재 공간에서 가장 가까운 5 개의 호텔을 상위 5 로 추천해야한다 (콘텐츠 기반 필터링).\n- Create a pairwise ranking matrix factorization model of user to hotel cluster (Collaborative filtering)\n사용자와 호텔 군집의 쌍별 순위 행렬 인수 분해 모델 작성하기 (협업 필터링)\n- Factorization machine of the dependent categoricals and numericals (Hybrid)\n종속 범주 및 숫자의 인수 분해 기계 (하이브리드)"},{"metadata":{},"cell_type":"markdown","source":"## * 컬럼 내용\n#### 범주 = [site_name : Expedia 판매 시점의 ID(사용자가 사용한 사이트 국가), posa_continent : site_name과 연관된 대륙의 ID, user_location_country : 사용자가 위치한 국가의 ID, user_location_region : 사용자가 위치한 지역의 ID, user_location_city : 사용자가 위치한 도시의 ID, is_mobile :사용자가 모바일 장치에서 연결한 경우 1, 아니라면 0, is_package : 클릭, 예약이 패키지의 일부로 생성된 경우(항공편과 결합시) 1, 아니라면 0, channel : 마케팅 채널의 ID,srch_destination_type_id : 목적지 유형, hotel_continent : 호텔 대륙, hotel_country : 호텔 국가, hotel_market : 호텔 마켓, srch_destination_id : 호텔 검색이 수행된 목적지의 ID]\n\n숫자 = [orig_destination_distance : 검색시 호텔과 고객간의 물리적 거리. Null은 거리를 계산 할 수 없음의 의미, srch_adults_cnt : 호텔 객실에 지정된 성인 수, srch_rm : 검색에 지정된 호텔 객실 수, cnt : 총 인원수]\n\n사용자 ID = [user_id : 사용자의 ID]\n예약여부 = [is_booking :  예약인 경우 1, 아니라면 0]\n호텔 군집 = [hotel_cluster : 호텔 그룹,군집]"},{"metadata":{"trusted":true},"cell_type":"code","source":"CATEGORICALS = [\"site_name\", \"posa_continent\", \"user_location_country\", \"user_location_region\", \"user_location_city\", \"is_mobile\", \"is_package\", \"channel\", \n               \"srch_destination_type_id\", \"hotel_continent\", \"hotel_country\", \"hotel_market\", \"srch_destination_id\"]\nNUMERICALS = [\"orig_destination_distance\", \"srch_adults_cnt\", \"srch_children_cnt\", \"srch_rm_cnt\", \"cnt\"]\nUSER_ID = \"user_id\"\nIS_BOOKING = \"is_booking\"\nHOTEL_CLUSTER = \"hotel_cluster\"","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"ax = data[HOTEL_CLUSTER].value_counts().plot.bar(color='dodgerblue')\nax.set_xticklabels([])\nplt.title(\"Hotel clusters' clicks - Looks like the beginnings of a power-law distribution\");","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"data_cat_dummies = pd.get_dummies(pd.get_dummies(data[CATEGORICALS].astype('category')))","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"def name_scores(featurecoef, col_names, label=\"Score\", sort=False):\n    df_feature_importance = pd.DataFrame([dict(zip(col_names, featurecoef))]).T.reset_index()\n    df_feature_importance.columns = [\"Feature\", label]\n    if sort:\n        return df_feature_importance.sort_values(ascending=False, by=label)\n    return df_feature_importance","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"from sklearn.feature_selection import chi2\n\nsample_n = 10000\ndata_cat_dummies_sample = data_cat_dummies.sample(sample_n)\nchi2_scores = chi2(data_cat_dummies_sample, data[HOTEL_CLUSTER].loc[data_cat_dummies_sample.index])\ndf_chi2_scores = name_scores(chi2_scores[0], data_cat_dummies_sample.columns)\ndf_chi2_scores = df_chi2_scores.sort_values(by=\"Score\", ascending=False)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"# get the top 100 features and graph the categorical\n# 상위 100개를 뽑은 후 그래프로 범주화한다.\nn = 100\ntop_n_features = df_chi2_scores[:n][\"Feature\"]","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"top_n_features.apply(lambda s : ' '.join(s.split(\"_\")[:len(s.split(\"_\"))-1])).value_counts()","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"* The graph below gets the features whose dummy variables contain the most entries in the top 100 by chi2 scores.\n아래 그래프는 더미 변수가 chi2 scores로 상위 100 개의 항목을 가장 많이 포함하는 기능을 보여준다.\n* Let's graph hotel market, country and user's city with respect to the hotel clusters.\n호텔 클러스터와 관련하여 호텔 시장, 국가 및 사용자 도시를 그래프로 나타내겠다.\n* We'll dedicate a section for srch_destination_id, but that seems very relevant.\nsrch_destination_id에 대한 섹션을 제공할 것이고 이는 매우 관련성이있는 것으로 보인다.\n* For ease of readability, we'll only get the top 5 hotel_clusters, their market, country and user location.\n쉽게 읽을 수 있도록 상위 5 개의 hotel_clusters, 시장, 국가 및 사용자 위치만을 뽑아낼 것이다."},{"metadata":{},"cell_type":"markdown","source":"## Some basic findings 기본적인 발견 몇가지\n- Looks like user city ~17 and ~90 is very active all throughout the different hotel clusters\n사용자 도시 ~ 17과 ~ 90은 다른 호텔 클러스터 전체에서 매우 활발한 것처럼 보인다.\n- Looks like hotel_market ~59 and 62 are related to many hotel_clusters\n hotel_market ~ 59 및 62가 많은 hotel_clusters와 관련이있는 것 같다.\n- Looks like hotel_country ~8 is related to many hotel_clusters\nhotel_country ~ 8은 많은 hotel_clusters와 관련이 있다."},{"metadata":{"trusted":true},"cell_type":"code","source":"def create_matrix(data, group_column, val_column):\n    grouped_data = data.groupby(group_column)[val_column].value_counts()\n    grouped_data = grouped_data.groupby(level=0).nlargest(3)\n    grouped_data.index = grouped_data.index.droplevel(0)\n    # transfrom to a square matrix\n    grouped_data_unstacked = grouped_data.unstack()\n    return grouped_data_unstacked.fillna(0)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"hotel_user_city_vc_matrix = create_matrix(data, \"hotel_cluster\", \"user_location_city\")\nhotel_market_vc_matrix = create_matrix(data, \"hotel_cluster\", \"hotel_market\")\nhotel_country_vc_matrix = create_matrix(data, \"hotel_cluster\", \"hotel_country\")\nhotel_continent_vc_matrix = create_matrix(data, \"hotel_cluster\", \"hotel_continent\")\n\n# hotel user city\nfig, axes = plt.subplots(2, 2, figsize=(15, 15))\n\naxes[0][0].set_title(\"Looks like user city ~17 and ~90 is very active\\n all throughout the different hotel clusters\")\naxes[0][0].imshow(hotel_user_city_vc_matrix, cmap='gray')\n\naxes[0][1].set_title(\"Looks like hotel_market ~59 and 62 are\\nrelated to many hotel_clusters\")\naxes[0][1].imshow(hotel_market_vc_matrix, cmap='gray')\n\naxes[1][0].set_title(\"Looks like hotel_country ~8 is\\nrelated to many hotel_clusters (France?)\")\naxes[1][0].imshow(hotel_country_vc_matrix, cmap='gray');\n\naxes[1][1].set_title(\"I think hotel_continent 1 is Europe!\")\naxes[1][1].imshow(hotel_continent_vc_matrix, cmap='gray');","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"# Numericals 숫자\n- Since orig_destination_distance has null values, let's see its distribution then decide on the imputation method.\norig_destination_distance(사용자와 호텔 간의 물리적 거리)에 null 값이 있으므로 분포를 확인한 후 대치 방법을 결정하겠다."},{"metadata":{"trusted":true},"cell_type":"code","source":"sns.distplot(data[\"orig_destination_distance\"].dropna())\n\nplt.title(\"Majority of destination places are close to the origin. \\nThus, let's just use the median for imputation.\");","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"sample_n = 10000\ndata_numericals_sample = data[NUMERICALS].sample(sample_n)\nchi2_scores = chi2(data_numericals_sample.fillna(data_numericals_sample.median()), data[HOTEL_CLUSTER].loc[data_numericals_sample.index])\ndf_chi2_scores = name_scores(chi2_scores[0], data_numericals_sample.columns)\n\ndf_chi2_pvalues = name_scores(chi2_scores[1], data_numericals_sample.columns)\ndf_chi2_pvalues.columns = [\"Feature\", \"PValue\"]\n\ndf_chi2_scores.merge(df_chi2_pvalues).sort_values(by=\"Score\", ascending=False)","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"### Hypotheses: 가설\n- Distance is important to travelers. Hotel clusters seem to be dependent on the distance.\n거리는 여행자에게 중요합니다. 호텔 클러스터는 거리에 의존하는 것 같다.\n- Certain hotel clusters seem to be children friendly\n특정 호텔 클러스터는 유아친화적인 것 같다.\n- Certain hotel_clusters seem also to rake in \"bandwagoners\" (thru cnt variable)\n특정 hotel_clusters는 1위만을 쫓는 사람들을 끌고 오는 것 같다.(cnt 변수를 통해)"},{"metadata":{"trusted":true},"cell_type":"code","source":"sns.boxplot(data=data, x=HOTEL_CLUSTER, y=\"orig_destination_distance\")\n\nplt.title(\"There's a lot of outlier distances for each hotel_cluster\");","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"data_cluster = data[[HOTEL_CLUSTER]].copy()\ndata_cluster[\"cluster_num\"] = pd.cut(data_cluster[HOTEL_CLUSTER], bins=4, labels=range(4))","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"clusters_1 = data[data[HOTEL_CLUSTER].isin(data_cluster.loc[data_cluster[\"cluster_num\"] == 0, HOTEL_CLUSTER])]\nclusters_1_unstacked = clusters_1.groupby(HOTEL_CLUSTER)[\"srch_children_cnt\"].value_counts().unstack().fillna(0)\n\nclusters_1_unstacked= clusters_1_unstacked.drop(0, axis=1).sort_values(by=2,)\nax = clusters_1_unstacked.plot.barh(stacked=True)\nax.legend(loc='upper right')\n\nplt.title(\"Some hotel_clusters are more perceived to be family friendly\", );","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"# What is the relationship of srch_destination_id to hotel_clusters?\nsrch_destination_id와 hotel_clusters의 관계는 무엇인가?"},{"metadata":{"trusted":true},"cell_type":"code","source":"print(\"Search destination id's nunique: \", data[\"srch_destination_id\"].nunique())\nprint(\"Search destination types nunique: \", data[\"srch_destination_type_id\"].nunique())","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"## For each hotel cluster, how is srch type related?\n각 호텔 클러스터마다 srch 유형은 어떤 관련이 있는가?"},{"metadata":{"trusted":true},"cell_type":"code","source":"hotel_search_type_matrix = create_matrix(data, HOTEL_CLUSTER, \"srch_destination_type_id\")\nprint(\"Looks like the search types tend around 1 and 6.\")\ndisplay(hotel_search_type_matrix[:10])\nplt.imshow(hotel_search_type_matrix, cmap='gray');","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"## For each hotel cluster, how is search_id related. How many unique search ids are there for each hotel cluster?\n각 호텔 클러스터에 대해 search_id는 어떻게 관련되어 있는가? 각 호텔 클러스터마다 몇 개의 고유한 검색 ID가 있나?\n\n- Seems like there is a non-trivial amount of search id of just 1. These may be the \"weird\" searches that corresponded to some hotel clusters.\n적은 양의 검색 ID가 1인 것처럼 보인다. 이는 일부 호텔 클러스터에 해당하는 이상한 검색일 수 있다.\n- On the other hand, there's a lot of search ids per hotel cluster. Seems we can work with this for our content-based filtering algorithm. We can spend time tweaking embeddings for each hotel clsuter. Or we can just average them out.\n반면에 호텔 클러스터당 많은 검색 ID가 있다. 콘텐츠 기반 필터링 알고리즘을 위해 이 작업을 수행 할 수 있다. 각 호텔 클러스터의 임베딩을 조정하는 데 시간을 할애할 수 있다. 아니면 그냥 평균을 내릴 수도 있다."},{"metadata":{"trusted":true},"cell_type":"code","source":"hotel_to_search_n = data.groupby(HOTEL_CLUSTER)[\"srch_destination_id\"].nunique()\nax = sns.distplot(hotel_to_search_n, bins=50, kde=False)\nax.set_xlabel(\"Number of unique search IDs per hotel cluster\")\n\ndisplay(data.groupby(HOTEL_CLUSTER)[\"srch_destination_id\"].nunique().describe().to_frame(\n    \"Stats of the number of unique search IDs per hotel cluster\"))","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"# Latent space variables look like a 10-20 'expressed' variables out of a hundred fifty\n잠재 공간 변수는 150개 중 10-20 개의 명시된 변수처럼 보인다."},{"metadata":{"trusted":true},"cell_type":"code","source":"destinations = pd.read_csv(\"/kaggle/input/expedia-hotel-recommendations/destinations.csv\")","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"def create_latent_search_img(index, ax):\n    # to make the image 10x15, we create a 150th feature with the mean of the array\n    img = np.array(destinations.loc[index].values[1:].tolist() + [destinations.loc[index].values[1:].mean()])\n    img = img.reshape((15,10))\n    sns.heatmap(img, cmap='gray', ax=ax)\n    ax.set_xticklabels([])\n    ax.set_yticklabels([])\n    return ax","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"ax1 = create_latent_search_img(0, plt.subplot(2, 3, 1))\nax2 = create_latent_search_img(1, plt.subplot(2, 3, 2))\nax3 = create_latent_search_img(2, plt.subplot(2, 3, 3))\nax4 = create_latent_search_img(3, plt.subplot(2, 3, 4))\nax5 = create_latent_search_img(3, plt.subplot(2, 3, 5))\nax6 = create_latent_search_img(3, plt.subplot(2, 3, 6))","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"# Let's average out the search ids per hotel cluster\n호텔 클러스터당 검색 ID를 평균합니다.\n\n- Keep in mind the minimum number of unique search ids per hotel_cluster is 5, so we should have unique \"feature images\" per hotel_cluster.\nhotel_cluster당 최소 고유 검색 ID 수는 5개이므로 hotel_cluster당 고유한 \"기능 이미지\"가 있어야한다.\n- When we search, we may click or not click on a hotel cluster. We take into account that act of clicking, which means the more search ids associated to a hotel_cluster, the more expressed the variables of those ids should be.\n검색 할 때 호텔 클러스터를 클릭하거나 클릭하지 않을 수 있다. 클릭 행위를 고려한다. 즉, hotel_cluster와 관련된 검색 ID가 많을수록 해당 ID의 변수가 더 많이 표현되어야한다.\n   - We can either make this a sum or weighted mean aggregation operation. This can be for exploration in the modeling stage.\n이것을 합산 또는 가중 평균 집계 연산으로 만들 수 있다. 모델링 단계에서 탐색 할 수 있다.\n   - For now, we'll do a sum.\n   일단, 합계토록 한다."},{"metadata":{"trusted":true},"cell_type":"code","source":"def get_latent_search_hotel_array(hotel_cluster_index):\n    values = data.loc[data[HOTEL_CLUSTER] == hotel_cluster_index, \"srch_destination_id\"].to_frame().merge(destinations)\n    values = values.drop(\"srch_destination_id\", axis=1)\n    values = values.sum()\n    return values\n\ndef create_latent_search_hotel_image(hotel_cluster_index, ax):\n    img = get_latent_search_hotel_array(hotel_cluster_index)\n    img = np.array(img.tolist() + [img.mean()])\n    img = img.reshape((15,10))\n    \n    ax.imshow(img, cmap='gray')\n    ax.set_title(\"Cluster \" + str(hotel_cluster_index))\n    ax.set_xticklabels([])\n    ax.set_yticklabels([])\n    return ax","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"hotel_arrays = []\nfor i in range(data[HOTEL_CLUSTER].nunique()):\n    hotel_arrays.append(get_latent_search_hotel_array(i))\nhotel_arrays = np.array(hotel_arrays)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"from scipy.cluster import hierarchy\nimport matplotlib.pyplot as plt\n\nZ = hierarchy.linkage(hotel_arrays, 'single')\nplt.figure(figsize=(20, 8))\ndn = hierarchy.dendrogram(Z)\n\n# We change the fontsize of minor ticks label \nplt.tick_params(axis='both', which='major', labelsize=10)\nplt.tick_params(axis='both', which='minor', labelsize=10)\nplt.xticks(rotation=0)\nplt.show()","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"fig = plt.figure(figsize=(20, 20))\n\nfor i in range(data[HOTEL_CLUSTER].nunique()):\n    create_latent_search_hotel_image(i, fig.add_subplot(10, 10, i+1))","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"# Analysis: 분석\nThere's some minute differences across clusters although there are the same latent variables that are always expressed.\n항상 표현되는 동일한 잠재 변수가 있지만 군집마다 약간의 차이가 있습니다.\nFrom the dendrogram, some clusters are very close while those on the left seem very unique.\n덴드로그램(트리 다이어그램)에서 일부 클러스터는 매우 가깝고 왼쪽의 클러스터는 매우 독창적입니다.\n\n# Stats for Collaborative Filtering 필터링 통계\nLooks like we can do CF - Matrix Factorization here. Non-zero percentage is 28%, very trivial for CF.\nCF-Matrix Factorization을 할 수있는 것처럼 보입니다. 0이 아닌 백분율은 28 %이며 CF의 경우 매우 사소합니다.\nWe should use dimensionality lower than 100 as to not overfit.\n과적합을 피하기 위해 100보다 작은 치수를 사용해야합니다."},{"metadata":{"trusted":true},"cell_type":"code","source":"user_id_col = \"user_id\"\nitem_id_col = HOTEL_CLUSTER\nratings = data[[user_id_col, item_id_col]]\n\nnum_users = ratings[user_id_col].nunique()\nnum_items = ratings[item_id_col].nunique()\npossible_combinations = num_users * num_items\nnnz = len(ratings)\nnnz_percent = nnz / possible_combinations\n\nprint(\"Num Users:\", num_users)\nprint(\"Num Items:\", num_items)\nprint(\"Sparsity:\", nnz_percent)\nprint(\"Not very sparse. CF will work wonders here.\")\n\n# average number of hotel_clusters per user\n# 사용자 당 평균 호텔 군집 수\nhotel_per_user = ratings.groupby(user_id_col)[item_id_col].nunique()\n\nfig = plt.figure(figsize=(10, 8))\nax = fig.add_subplot(211)\nsns.distplot(hotel_per_user, kde=False, ax=ax)\nax.set_title(\"Mean number of clusters per user: {:.2f}\".format(hotel_per_user.mean()))\n\n# average number of users per hotel\n# 호텔당 평균 사용자 수\nuser_per_hotel = ratings.groupby(item_id_col)[user_id_col].nunique()\nax = fig.add_subplot(212)\nsns.distplot(user_per_hotel, kde=False, ax=ax)\nax.set_title(\"Mean number of users per cluster: {:.2f}\".format(user_per_hotel.mean()))\n\nfig.tight_layout()","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"","execution_count":null,"outputs":[]}],"metadata":{"kernelspec":{"language":"python","display_name":"Python 3","name":"python3"},"language_info":{"pygments_lexer":"ipython3","nbconvert_exporter":"python","version":"3.6.4","file_extension":".py","codemirror_mode":{"name":"ipython","version":3},"name":"python","mimetype":"text/x-python"}},"nbformat":4,"nbformat_minor":4}