{"cells":[{"metadata":{"_uuid":"5c4f796648e1db6810665d24e425a49115a20fec"},"cell_type":"markdown","source":"# Google Analytics Customer Revenue Prediction"},{"metadata":{"trusted":true,"_uuid":"82d58315ae4f62eaf1a1bd43f0ddab8da09a6f77"},"cell_type":"code","source":"# Import libraries to use\n\n# Common imports\nimport numpy as np\nimport os\nimport pandas as pd\nfrom sklearn.model_selection import train_test_split\n#from preprocess import read_data, json_read\n\nfrom datetime import datetime # To access datetime\n\nimport warnings                # To ignore the warning\nwarnings.filterwarnings(\"ignore\")\n\n# To plot pretty figures\n%matplotlib inline\nimport matplotlib\nimport matplotlib.pyplot as plt\nimport seaborn as sns","execution_count":null,"outputs":[]},{"metadata":{"_uuid":"243e998822cfbec020b74df0eefa20078984996f"},"cell_type":"markdown","source":"## Auxiliary Functions"},{"metadata":{"trusted":true,"_uuid":"8072680f59ff44ab83c808fa4102fdbcf87549d2"},"cell_type":"code","source":"import pandas as pd\nimport numpy as np\nimport json\nimport os # it's a operational system library, to set some informations\n\ndef read_data(file_path, file_name, data_format):\n    \"\"\"\n    Parameters:\n    -----------\n    file_path: str \n               where the datafile is\n    file_name: str\n               file name of the datafile\n    data_format: dict\n                 format of data to import\n                 \n    Return\n    ------\n    df: dataframe\n        the df after preprocessing\n    \"\"\"\n    # Load the data\n    df = pd.read_csv(file_path + file_name, dtype = data_format)\n    \n    # Printing the shape of dataframes that was imported     \n    print(\"Loaded file at {}, and dataframe with shape {}\".format(file_path + file_name, df.shape))\n    \n    return df\n\n\ndef json_read(df, field_name, extract_field, new_field_name):\n    \"\"\"\n    Read semi-structured JSON data\n    \n    Parameters:\n    ----------\n    df: dataframe\n        the dataframe needed to process\n    field_name: str\n                column to read json data\n    extract_field: str \n                   info to extract from json data format\n    new_field_name: str\n                    add a column for data extract from json data format\n                    \n    Return\n    ------\n    df: dataframe\n        the df after extracting data from json data format and adding to a new column, called new_field_name\n    \"\"\"\n    df[new_field_name] = pd.DataFrame(df[field_name].apply(json.loads).tolist())[[extract_field]]\n    return df","execution_count":null,"outputs":[]},{"metadata":{"_uuid":"4477c12e20b6c3eb75e95fb8846ccc49938d4626"},"cell_type":"markdown","source":"# 1. Load Data"},{"metadata":{"_uuid":"995dd25e4d9e45019a701893a3b19bb4a064e690"},"cell_type":"markdown","source":"Load training set:"},{"metadata":{"trusted":true,"_uuid":"c01ace8622b803e544841aff1ca2f615c8f652b1"},"cell_type":"code","source":"chunksize = 600000\nfor chunk in pd.read_csv('../input/train_v2.csv', chunksize=chunksize):\n    df_train = chunk\n    break\n    \ndf_train.head()","execution_count":null,"outputs":[]},{"metadata":{"trusted":true,"_uuid":"1a946cadec551b16a75ac3372085d93213733001"},"cell_type":"code","source":"# Check data type in each column\ndf_train.dtypes","execution_count":null,"outputs":[]},{"metadata":{"scrolled":true,"trusted":true,"_uuid":"0d8a4b42fd106c470b2a22a025f2d43f80211c09"},"cell_type":"code","source":"df_train.describe()","execution_count":null,"outputs":[]},{"metadata":{"trusted":true,"_uuid":"1f236e7725c6dbd8a08b9b2f64153ebeb2ac7537"},"cell_type":"code","source":"# Check memory usage in MB\ndf_train.memory_usage(deep=True)* 1e-6","execution_count":null,"outputs":[]},{"metadata":{"trusted":true,"_uuid":"8d612b0f47e5cd4f849bdeaa296c57e9d407fb58"},"cell_type":"code","source":"# Estimate total memory usage\nusage = df_train.memory_usage(deep=True).sum() * 1e-6\nprint('Memory usage is {} Gb'.format(usage/1000))","execution_count":null,"outputs":[]},{"metadata":{"_uuid":"dbe79a30fbc72627cb7caad9dc6226d5eb0b6396"},"cell_type":"markdown","source":"## Data Fields\n\n- **fullVisitorId**- A unique identifier for each user of the Google Merchandise Store.\n- **channelGrouping** - The channel via which the user came to the Store.\n- **date** - The date on which the user visited the Store.\n- **device** - The specifications for the device used to access the Store.\n- **geoNetwork** - This section contains information about the geography of the user.\n- **sessionId** - A unique identifier for this visit to the store.\n- **socialEngagementType** - Engagement type, either \"Socially Engaged\" or \"Not Socially Engaged\".\n- **totals** - This section contains aggregate values across the session.\n- **trafficSource** - This section contains information about the Traffic Source from which the session originated.\n- **visitId** - An identifier for this session. This is part of the value usually stored as the _utmb cookie. This is only unique to the user. For a completely unique ID, you should use a combination of fullVisitorId and visitId.\n- **visitNumber** - The session number for this user. If this is the first session, then this is set to 1.\n- **visitStartTime** - The timestamp (expressed as POSIX time).\n- **hits** - This row and nested fields are populated for any and all types of hits. Provides a record of all page visits. (new in version 2 data set)\n- **customDimensions** - This section contains any user-level or session-level custom dimensions that are set for a session. This is a repeated field and has an entry for each dimension that is set. (new in version 2 data set)\n"},{"metadata":{"trusted":true,"_uuid":"b567b57e505e5965ffd7aa248794b8ddeeeda315"},"cell_type":"code","source":"chunksize = 100\nfor chunk in pd.read_csv('../input/test_v2.csv', chunksize=chunksize):\n    df_test = chunk\n    break\n\ndf_test.head()","execution_count":null,"outputs":[]},{"metadata":{"trusted":true,"_uuid":"bdc8ddef4d34631344a5b1cfbfa976face38142d"},"cell_type":"code","source":"del df_test","execution_count":null,"outputs":[]},{"metadata":{"_uuid":"8372eb15b5f5ce086b73c090f1b87645708e78b3"},"cell_type":"markdown","source":"\n## What am I predicting?\n\nThe following is the sample submission."},{"metadata":{"trusted":true,"_uuid":"d4fa6502e38e6b61c97529d2445db0d477aaed68"},"cell_type":"code","source":"df_submit = pd.read_csv('../input/sample_submission_v2.csv')\ndf_submit.head()","execution_count":null,"outputs":[]},{"metadata":{"trusted":true,"_uuid":"0b854da849a8d05623a3059a7cf539f308ffd939"},"cell_type":"code","source":"del df_submit","execution_count":null,"outputs":[]},{"metadata":{"_uuid":"eb87ece380c3b7d613a4111ce50d7c1ad6f79cbd"},"cell_type":"markdown","source":"For each `fullVisitorId` in the test set, we must predict the **natural log** of their total revenue in `PredictedLogRevenue`. The submission file should contain a header and have the following format:\n\n```\nfullVisitorId,PredictedLogRevenue\n0000000259678714014,0\n0000049363351866189,0\n0000053049821714864,0\netc.\n```\n\nWe are predicting the natural log of the **sum of all transactions per user**. For every user in the test set, the target is:\n\n$$ y_{user}=\\sum_{i=1}^{n}transaction_{user_i}$$\n\n$$ target_{user}=\\ln(y_{user}+1)$$\n\nwhere $n$ is the number of times a specific customer vistied the GStore. \n\n**Why do we choose above metric to gauage the performance?**\n\nSince each transaction is a large value, if we use those values directly, then it will not be sensitive to the minor changes! Thus, we  apply natural log to $y_{user}$. The reason why we consider $y_{user}+1$ is because some users may not purchase anything and $y_{user}=0$. Then $\\ln (y_{user})$ will not be well-defined! In contrast, $\\ln (y_{user}+1) = 0$ when $y_{user} = 0$. \n\nNote that Kaggle has updated dataset, `train_v2.csv` and `test_v2.csv`, and the training dataset does NOT contain data for December 1st 2018 to January 31st 2019. You must identify the unique fullVisitorIds in the provided test_v2.csv and make predictions for them for those unseen months."},{"metadata":{"trusted":true,"_uuid":"6a62de97f5c098deb72e008317ef05c7a3641b01"},"cell_type":"code","source":"df_train.loc[0,'geoNetwork']","execution_count":null,"outputs":[]},{"metadata":{"trusted":true,"_uuid":"3bb78bc5930e4fbc0d8ed0c7737d7dcbb8ce6c47"},"cell_type":"code","source":"df_train.loc[10,'device']","execution_count":null,"outputs":[]},{"metadata":{"_uuid":"331c649a9eda6cb7d8f7750ada5d3d0f9a9385f4"},"cell_type":"markdown","source":"Note that `deviceCategory` and `isMobile` are related actually. If `deviceCategory` is mobile or tablet, then `isMobile` is true. If `deviceCategory` is desktop, then `isMobile` is false. We will examine these two features later to see whether it's necessary to consider `deviceCategory` or not."},{"metadata":{"trusted":true,"_uuid":"a0df54761594bc963e33d9c9b7afaa94c8cacdf8"},"cell_type":"code","source":"df_train.loc[101,'device']","execution_count":null,"outputs":[]},{"metadata":{"trusted":true,"_uuid":"fb94891f2551291a9930525310fb39bac3b01e3e"},"cell_type":"code","source":"df_train.loc[80,'totals']","execution_count":null,"outputs":[]},{"metadata":{"trusted":true,"_uuid":"a8fa3c93048979241853873e49945bdd1693b260"},"cell_type":"code","source":"df_train.loc[102,'trafficSource']","execution_count":null,"outputs":[]},{"metadata":{"_uuid":"1865be8b3f3ab8e9c0e495a79d80a974d249a7a7"},"cell_type":"markdown","source":"In **device, geoNetwork, totals, trafficSource** fields, they are semi-structured JSON data.\n\n- **device**: browser, browserVersion, browserSize, operatingSystem, operatingSystemVersion, isMobile, mobileDeviceBranding, mobileDeviceModel, mobileInputSelector, mobileDeviceInfo, mobileDeviceMarketingName, flashVersion, language, screenColors, screenResolution, deviceCategory\n- **totals**: visits, hits, pageviews, newVisits, bounces\n- **geoNetwork**: continent, subContinent, country, region, metro, city, cityId, networkDomain, latitude, longitude, networkLocation\n- **trafficSource**: campaign, source, medium, keyword, adwordsClickInfo, referralPath\n"},{"metadata":{"trusted":true,"_uuid":"115b1283e7355f5ba0485773f66c5cce5120451a"},"cell_type":"code","source":"# Extract revenue from transactionRevenue from totals field\nfield_name = 'totals' \nextract_field = 'transactionRevenue'\nnew_field_name = 'revenue'\n\njson_read(df_train, field_name, extract_field, new_field_name).head()","execution_count":null,"outputs":[]},{"metadata":{"trusted":true,"_uuid":"85e3adc398d8e6f7dfcf7c4b1050ad570651850a"},"cell_type":"code","source":"# Estimate total memory usage\nusage = df_train.memory_usage(deep=True).sum() * 1e-6\nprint('Memory usage is {} Gb'.format(usage/1000))","execution_count":null,"outputs":[]},{"metadata":{"trusted":true,"_uuid":"0528b0e28d094de4c115904e28dfd4598c34ca0e"},"cell_type":"code","source":"# Check missing values in revenue field\ndf_train['revenue'].isnull().sum()","execution_count":null,"outputs":[]},{"metadata":{"_uuid":"884fe31b361ab47454e148ff346842f8ec1ad43a"},"cell_type":"markdown","source":"There are 593602 missing values out of 600000. This is highly **imbalanced dataset**. We have to keep this in mind when we build model to do further analysis. We denote these missing values by 0 since they may be events that customers visit gstore without purchasing any products."},{"metadata":{"trusted":true,"_uuid":"cfdd757ea82baaaa852f038ba0398ffbfc88da49"},"cell_type":"code","source":"# Fill in missing data with zeros\ndf_train['revenue'] = df_train['revenue'].fillna(0)\ndf_train.head(7)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true,"_uuid":"12b6e2d90ffca8968f045a5b9242e940484c284c"},"cell_type":"code","source":"df_train['revenue'] = df_train['revenue'].astype('int64')\ndf_train.dtypes","execution_count":null,"outputs":[]},{"metadata":{"_uuid":"255dea42850028e2695678a886d6e0dc716a8373"},"cell_type":"markdown","source":"In order to determine what factors affect whether customers purchase things or not when they visit gstore, we set 0 when revenue is 0 (not purchasing) and 1 when revenue is not 0 (purchasing) as shown in column `Buy`."},{"metadata":{"trusted":true,"_uuid":"f05c45ae9cdb17dbf1f1a41f470332fb55907522"},"cell_type":"code","source":"df_train['Buy'] = df_train['revenue'].apply(lambda x: 1 if x != 0 else 0)\ndf_train.head()","execution_count":null,"outputs":[]},{"metadata":{"trusted":true,"_uuid":"287729cf1d3ea5d40f84bcfe903bf66e50cbaa4a"},"cell_type":"code","source":"# Estimate total memory usage\nusage = df_train.memory_usage(deep=True).sum() * 1e-6\nprint('Memory usage is {} Gb'.format(usage/1000))","execution_count":null,"outputs":[]},{"metadata":{"_uuid":"dca83c081495283bc2d7a3e7a564518ce379b163"},"cell_type":"markdown","source":"# 2. Data Explore, Clean Data and Feature Engineering\n\n## 2.1 channelGrouping feature"},{"metadata":{"trusted":true,"_uuid":"318eb8a221e6b09c233b6b30167df155f06cd995"},"cell_type":"code","source":"df_train.channelGrouping.value_counts().plot(kind=\"bar\",title=\"channelGrouping distribution\",figsize=(8,8),rot=25,colormap='Paired')","execution_count":null,"outputs":[]},{"metadata":{"_uuid":"75b9aef9d199bab61b40531a650295526108f695"},"cell_type":"markdown","source":"From above figure, we can see that most of customers used `Organic Search` channel to come to gstore, and `Social` channel is the second one. But does it mean there are more customers purchasing goods in gstore from `Organic Search` channel? Let's find out below.\n\n### channelGrouping vs. revenue"},{"metadata":{"trusted":true,"_uuid":"d28b9079a018bdcec8d1d16784d7ffd336b506c9"},"cell_type":"code","source":"df_channel = df_train[['channelGrouping','revenue', 'Buy']]\n(df_channel.set_index('channelGrouping').groupby(level=0)['revenue'].agg({'mean': np.average, 'median':np.median, 'std':np.std, \n                                                                          'max': np.max, 'min': np.min}) )","execution_count":null,"outputs":[]},{"metadata":{"_uuid":"ddf46485329175ce4fbe561fcdc73e081ef87570"},"cell_type":"markdown","source":"According to mean and median of revenue for each channelGrouping, it is clear that in each channelGrouping, there are many customers visiting gstore without purchasing! **The distribution of data in each channel is highly right-skewed**."},{"metadata":{"trusted":true,"_uuid":"4876ac7fad5ec891310431325b0b42e0cb752b44"},"cell_type":"code","source":"# Visualize the data by drawing boxplot grouped by a categorical variable:\nsns.boxplot(x='revenue', y='channelGrouping', data=df_channel)","execution_count":null,"outputs":[]},{"metadata":{"_uuid":"8c77ca7abda630a1b5b13f3b7651b1851b991380"},"cell_type":"markdown","source":"### Are those customers used specific channel most possible to buy goods in gstore?"},{"metadata":{"trusted":true,"_uuid":"e9d8a1384d9e712817cd85a48be49150998fe4db"},"cell_type":"code","source":"Channel_Buy = (df_channel.groupby(['channelGrouping', 'Buy'])['revenue'].agg({'Count':'count'}))\nChannel_Buy['Relative Frequency'] = Channel_Buy.groupby(level=0).apply(lambda x: 100*x/float(x.sum()))\nChannel_Buy","execution_count":null,"outputs":[]},{"metadata":{"_uuid":"6519f3e1b3f047b2ae75b39ab05ec9a43f04fe13"},"cell_type":"markdown","source":"From above results, we can conclude that **customers from Referral channel are most possible to buy goods in gstore per visit**. "},{"metadata":{"_uuid":"3eb354fab732168488193e353481077fc5155285"},"cell_type":"markdown","source":"### Did customers who purchased goods in gstore from specific channel buy more?\n\nIn order to answer this question, we show boxplot for categorical variable (channelGrouping) vs. numerical variable (revenue)."},{"metadata":{"trusted":true,"_uuid":"3e664517a6a13a799b4b4b4c9945d506ede2e11e"},"cell_type":"code","source":"# boxplot for categorical variable (channelGrouping) vs. numerical variable (revenue)\ndf_channel_Buy = df_channel[df_channel['Buy'] == 1]\nsns.boxplot(x='revenue', y='channelGrouping', data=df_channel_Buy)","execution_count":null,"outputs":[]},{"metadata":{"_uuid":"fb9e1f5a9ff0b1c27d47cf58ff1d7269e707fab6"},"cell_type":"markdown","source":"From above figure, we cannot see the relation between channelGrouping and revenue due to large range of revenue in each channel. Thus, we do log transformation for revenue in the following."},{"metadata":{"trusted":true,"_uuid":"087dd1c3c8db718c85a16c2ac304c94052ae88c7"},"cell_type":"code","source":"# Transform revenue by log function\ndf_channel_Buy['log(revenue)'] = df_channel_Buy['revenue'].apply(np.log)\nsns.boxplot(x='log(revenue)', y='channelGrouping', data=df_channel_Buy)","execution_count":null,"outputs":[]},{"metadata":{"_uuid":"ea658d2910f942a8cceaff7b72dcb5d4801a8bf3"},"cell_type":"markdown","source":"From above figure, we can see that **`revenue` has very weak relationship with `channelGrouping`** since median in each channel is close to each other. Although Affiliates has much different median, it is not reliable actually since only 2 visits with purchasing. "},{"metadata":{"trusted":true,"_uuid":"5c25a9cee3971ed360b850650f54a8f493490c9c"},"cell_type":"code","source":"# Delete useless dataframe\ndel df_channel_Buy, Channel_Buy, df_channel","execution_count":null,"outputs":[]},{"metadata":{"_uuid":"9e480c32182eb8b8aa1b945daa00d589d8681586"},"cell_type":"markdown","source":"## 2.2 date feature\n\nThere are two variables related to time and can be used in time dependent analyzes, or Time Series. Now we study the relation between date feature and counts of customers purchasing.\n\n\n**1) Hypothesis Generation**\n\na. There will be more customers purchasing goods during the holiday season, say Nov. and Dec.\n- Explanation - Purchasing rate will be higher during the holiday season.\n\nb. There will be more customers purchasing goods during the weekend.\n- Explanation - People need to work on weekdays so they may not have time to visit GStore.\n\nc. There will be higher transaction per visit during the holiday season, say Nov. and Dec.\n- Explanation - People will buy more goods when Thanksgiving, Christmas, and New Year coming."},{"metadata":{"_uuid":"2d33507fd1bc9d0f777140a0e4b95d34f15403d1"},"cell_type":"markdown","source":"**2) Feature Extraction**\n\nWe will extract further info from the `date`. We have seen earlier that the data type of `date` is int64. So first of all we have to change the data type to datetime format otherwise we can not extract features from it."},{"metadata":{"trusted":true,"_uuid":"71839bcc2c29fb5cf1d8638d19af265f47f0e32a"},"cell_type":"code","source":"df_train['date'] = pd.to_datetime(df_train['date'],format=\"%Y%m%d\") ","execution_count":null,"outputs":[]},{"metadata":{"trusted":true,"_uuid":"559eca00f143a5f7459132336331515631c86889"},"cell_type":"code","source":"df_train.head()['date']","execution_count":null,"outputs":[]},{"metadata":{"_uuid":"cf0739ec179c04887c4468c87692fb699a355700"},"cell_type":"markdown","source":" Let's extract the year, month, day from the `date` to validate our hypothesis."},{"metadata":{"trusted":true,"_uuid":"83f9da8cdc137f7120746fbc986ffb77441996cb"},"cell_type":"code","source":"df_time = df_train[['date', 'revenue', 'Buy']]\ndf_time['year'] = pd.DatetimeIndex(df_time['date']).year\ndf_time['month'] = pd.DatetimeIndex(df_time['date']).month\ndf_time['day'] = pd.DatetimeIndex(df_time['date']).day","execution_count":null,"outputs":[]},{"metadata":{"trusted":true,"_uuid":"0e982bfbe2175226060fc8f623c9354f5e281d77"},"cell_type":"code","source":"df_time.head()","execution_count":null,"outputs":[]},{"metadata":{"_uuid":"1602669170def31d219d7bcb51f1a55ec6c2bfda"},"cell_type":"markdown","source":"We made a hypothesis that customers will purchase more goods on weekday and weekend as well. So, let's make a weekend variable to visualize the impact of weekend on purchasing rate.\n\n - We will first extract the day of week from `date` and then based on the values we will assign whether the day is a weekend or not.\n\n - Values of 5 and 6 represents that the days are weekend.\n\n"},{"metadata":{"trusted":true,"_uuid":"72f4aac6182e0635fae3c92961ef759d1e822650"},"cell_type":"code","source":"df_time['DayOfWeek']=df_time['date'].dt.dayofweek\ntemp = df_time['date']","execution_count":null,"outputs":[]},{"metadata":{"_uuid":"d619620ac24ce7ca3eadc2a8768b4483988cfe26"},"cell_type":"markdown","source":"Let’s assign 1 if the day of week is a weekend and 0 if the day of week in not a weekend."},{"metadata":{"trusted":true,"_uuid":"c43d9be978cf0aa567da13036789a80e9074e94b"},"cell_type":"code","source":"def applyer(row):\n    if row.dayofweek == 5 or row.dayofweek == 6:\n        return 1\n    else:\n        return 0\n\ntemp2 = df_time['date'].apply(applyer)\ndf_time['weekend']=temp2","execution_count":null,"outputs":[]},{"metadata":{"_uuid":"1d9a7b3b6b65e7b644c7410e5633a797b072e126"},"cell_type":"markdown","source":"Let's look at the time series."},{"metadata":{"trusted":true,"_uuid":"67759837e4323b5d077205e377658ea548404bba"},"cell_type":"code","source":"df_time.index = df_time['date'] # indexing the Datetime to get the time period on the x-axis.\ndf_time.head()","execution_count":null,"outputs":[]},{"metadata":{"trusted":true,"_uuid":"a7b0803330a0fafab672c14666925cae284ee99f"},"cell_type":"code","source":"df_time_buy = df_time[df_time['Buy'] == 1]\ndf_time_buy.head()","execution_count":null,"outputs":[]},{"metadata":{"_uuid":"ce752a0cce36abaef7cd3c7286edf0abdf422490"},"cell_type":"markdown","source":"**3) Exploratory Analysis**\n\nOur first hypothesis was purchasing rate will be higher in holiday season."},{"metadata":{"trusted":true,"_uuid":"6e8a115db974175059f93fa1c0075dd440c07819"},"cell_type":"code","source":"# using an html hex string for color\ncolor = '#0099ff'\ndf_time.groupby(['year','month']).size().plot.bar(rot = 55, color=color)\nplt.ylabel('Visit Count')","execution_count":null,"outputs":[]},{"metadata":{"_uuid":"3d6cec4dca1f01d339ea3d44ef7ef8e60e0b92a4"},"cell_type":"markdown","source":"As we can see in above figure, **there are more customers visiting GStore in Oct. 2016**.   Are there any specific event such that there are more vists in Oct. 2016? But does it also implies higher purchasing rate in that month? Let's examine that in the following."},{"metadata":{"trusted":true,"_uuid":"385df51853d70b2849a1f27b8fde0cd57cc2fd4a"},"cell_type":"code","source":"df_time.groupby(['year','month'])['Buy'].mean().plot.bar(color=color)\nplt.ylabel('Purchasing Rate')","execution_count":null,"outputs":[]},{"metadata":{"_uuid":"6853bc5aa537fca61ec98302672be115c50f8a2a"},"cell_type":"markdown","source":"As we can see in above figure, **there's no clear pattern about relation between time and purchasing rate**."},{"metadata":{"_uuid":"95605e435b292726f8b0c5e43813e42ddf8c3c1d"},"cell_type":"markdown","source":"Next, we examine our second hypothesis, there will be more customers purchasing goods during the weekend."},{"metadata":{"trusted":true,"_uuid":"21db668963b2059f724c25ab38238b475319965d"},"cell_type":"code","source":"df_time.groupby('weekend')['Buy'].mean().plot.bar(color=color)\nplt.ylabel('Purchasing Rate')","execution_count":null,"outputs":[]},{"metadata":{"_uuid":"2c87d3d4fd109445438c99399f146a47991ccd06"},"cell_type":"markdown","source":"In above figure, 0 denotes the weekday and 1 denotes the weekend. It is clear that our second hypothesis is not right. From the figure, we can see that **there are higher purchasing rate in weekdays**. Probably, that is because when people work or people go to office on weekdays, they have more chances to access the electric devices to visit the Google Merchandise Store."},{"metadata":{"_uuid":"51f569ef7d31af0007cee8a4f2da034f19014289"},"cell_type":"markdown","source":"In the end, we examine whether there will be higher transaction per visit during the holiday season, say Nov. and Dec. in the following:"},{"metadata":{"trusted":true,"_uuid":"5adc4fba967f81a55cd906c51220541100866308"},"cell_type":"code","source":"daily_transaction_per_visit_df = df_time_buy[['date','revenue']].groupby(by=['date'],axis=0).mean()\nfig, axes = plt.subplots(figsize=(20,10))\naxes.set_title(\"Daily Transaction per Visit\")\naxes.set_ylabel(\"Transaction per Visit\")\naxes.set_xlabel(\"date\")\naxes.plot(daily_transaction_per_visit_df[\"revenue\"])","execution_count":null,"outputs":[]},{"metadata":{"_uuid":"ab63362da914f5546141f417b6ea2ea2c8c67224"},"cell_type":"markdown","source":"From above result, we can see that the transaction per visit do not have strong peak at Nov. and Dec. so our last hypothesis is wrong. "},{"metadata":{"trusted":true,"_uuid":"f1166a6282438b523e727a0b7b3dd1a61790ed34"},"cell_type":"code","source":"# Clean dataframe\ndel daily_transaction_per_visit_df, df_time_buy, df_time","execution_count":null,"outputs":[]},{"metadata":{"_uuid":"71c16a2d7011ec634d77755249cf3eac05287537"},"cell_type":"markdown","source":"## 2.3 device feature\n\nThe field, `device`, is stored in json format. It is necessary to extract its fields and analyze them. Using `json_read` function to deserializing json values."},{"metadata":{"trusted":true,"_uuid":"f5d20803c219584d2e7f72588ec3e817eec5d5ec"},"cell_type":"code","source":"df_train.iloc[10,2]","execution_count":null,"outputs":[]},{"metadata":{"_uuid":"ead21649fd3da476c336207e854eac57805a3452"},"cell_type":"markdown","source":"There are many keys in device attribute with values \"not available in demo dataset\" so we should ignore these features. \n\n### What device do customers frequently use to visit the store?\n\nIn order to answer this question, we consider `deviceCategory` in the following. Note that `isMobile` and `deviceCategory` are related. In what follows, we also examine whther using `deviceCategory` can gain more insight, or one can use `isMobile` instead."},{"metadata":{"trusted":true,"_uuid":"c67d352cbb6ceadf5edc8e6955a8e17bba8b0f84"},"cell_type":"code","source":"df_device = df_train[['device', 'revenue', 'Buy']]","execution_count":null,"outputs":[]},{"metadata":{"scrolled":true,"trusted":true,"_uuid":"6f4d78c32c877254f8239991503ebbe9ba3eca12"},"cell_type":"code","source":"# Extract deviceCategory from device field\nfield_name = 'device' \nextract_field = 'deviceCategory'\nnew_field_name = 'DeviceCategory'\n\njson_read(df_device, field_name, extract_field, new_field_name).head()","execution_count":null,"outputs":[]},{"metadata":{"trusted":true,"_uuid":"c8017acc847c1d50906519318be22ce4faf4b1c9"},"cell_type":"code","source":"# Check whether there's missing values in DeviceCategory column\npd.isna(df_device['DeviceCategory']).sum()","execution_count":null,"outputs":[]},{"metadata":{"trusted":true,"_uuid":"87c7eb015d4a571eae55436e934065f446c5ff81"},"cell_type":"code","source":"# using an html hex string for color\ncolor = '#0099ff'\ndf_device.groupby(['DeviceCategory']).size().plot.bar(rot = 0, color=color)\nplt.ylabel('Visit Count')","execution_count":null,"outputs":[]},{"metadata":{"_uuid":"05da0c1be8994be40840759e2cfd3ce19215004a"},"cell_type":"markdown","source":"As we can see in the above bar plot, **most of customers visit the store via desktop device**. Also, visit counts for tablet is very small compared to mobile and desktop, and tablet and mobile can be regarded as mobile devices. Hence, we can group mobile and tablet features together, and we can classify the device is mobile device or not. This is nothing but `isMobile` in `device` attribute. Let's extract `isMobile` from `device` in the following:"},{"metadata":{"scrolled":true,"trusted":true,"_uuid":"517e0a3f162bfa50041eb59052211909ec622c7a"},"cell_type":"code","source":"# Extract deviceCategory from device field\nfield_name = 'device' \nextract_field = 'isMobile'\nnew_field_name = 'IsMobile'\n\njson_read(df_device, field_name, extract_field, new_field_name).head()","execution_count":null,"outputs":[]},{"metadata":{"_uuid":"97a79162a195943307b1ca50e9cf4246f561173e"},"cell_type":"markdown","source":"From above table, we can see that `IsMobile` is False if  `DeviceCategory` is \"desktop\". In contrast, `IsMobile` is True if  `DeviceCategory` is \"mobile\" or \"tablet\"."},{"metadata":{"trusted":true,"_uuid":"da19aa7d2904c3347a46c143d1dbfd04ac3a7b2a"},"cell_type":"code","source":"# Estimate total memory usage\nusage = df_device.memory_usage(deep=True).sum() * 1e-6\nprint('Memory usage is {} Gb'.format(usage/1000))","execution_count":null,"outputs":[]},{"metadata":{"trusted":true,"_uuid":"aa04c3d9b881dc070a38eafd0efca9aa1015df97"},"cell_type":"code","source":"# Check data type in IsMobile\ndf_device['IsMobile'].dtypes","execution_count":null,"outputs":[]},{"metadata":{"_uuid":"a408efedd2a2b9d3269987c260784448ef8c287a"},"cell_type":"markdown","source":"### Is IsMobile related to purchasing rate?"},{"metadata":{"trusted":true,"_uuid":"8539c9129ba5b7f6e4d86ef50e95be0b5eddb17b"},"cell_type":"code","source":"df_device.groupby('IsMobile')['Buy'].mean().plot.bar(color=color, rot = 0)\nplt.ylabel('Purchasing Rate')","execution_count":null,"outputs":[]},{"metadata":{"_uuid":"111a76c1c23372cea075020e88e35c6cb59c9201"},"cell_type":"markdown","source":"From above result, we can see that customers using desktop are more willing to purchase in store. "},{"metadata":{"_uuid":"b61795a48af359be10fbf2e9155d4a877330fda3"},"cell_type":"markdown","source":"### Is IsMobile related to revenue per visit?"},{"metadata":{"trusted":true,"_uuid":"359f2d893722cd813f3bf3d71708d3a3ed68b20f"},"cell_type":"code","source":"df_device_Buy = df_device[df_device['Buy']==1]\ndf_device_Buy.head()","execution_count":null,"outputs":[]},{"metadata":{"trusted":true,"_uuid":"b33335376b389369765d529ba67da2c99477ae5f"},"cell_type":"code","source":"# Transform revenue by log function\ndf_device_Buy['log(revenue)'] = df_device_Buy['revenue'].apply(np.log)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true,"_uuid":"e6717e16dfd4a232e1671ee86628b0d2bf351937"},"cell_type":"code","source":"sns.boxplot(x='IsMobile', y='log(revenue)', data=df_device_Buy)","execution_count":null,"outputs":[]},{"metadata":{"_uuid":"291f001b9081ab947a986d0616feaecb241aeff8"},"cell_type":"markdown","source":"As we can see in the above box plot, the medians for mobile device and non-mobile device are very close to each other. This implies that revenue per visit is not correlated to `IsMobile` feature. "},{"metadata":{"trusted":true,"_uuid":"9e374539c95c139b5b1d06c5ac7891269f4a3192"},"cell_type":"code","source":"del df_device_Buy, df_device","execution_count":null,"outputs":[]},{"metadata":{"_uuid":"a364ba305687afb8955466269e5b9c972381a38e"},"cell_type":"markdown","source":"## 2.4 geoNetwork\n\nThe field, `geoNetwork`, is stored in json format. It is necessary to extract its fields and analyze them. Using json_read function to deserializing json values."},{"metadata":{"trusted":true,"_uuid":"7ca02e25c5dfa22b6377d708183b739a910716e8"},"cell_type":"code","source":"df_train.loc[21,'geoNetwork']","execution_count":null,"outputs":[]},{"metadata":{"trusted":true,"_uuid":"1c6e73d9487de87f8ccb65ebee3dd2ad07f1ec60"},"cell_type":"code","source":"df_geo = df_train[['geoNetwork', 'revenue', 'Buy']]\ndf_geo.head()","execution_count":null,"outputs":[]},{"metadata":{"trusted":true,"_uuid":"07fa293dcabbbbe0db3654943570770dc667b978"},"cell_type":"code","source":"# Estimate total memory usage\nusage = df_geo.memory_usage(deep=True).sum() * 1e-6\nprint('Memory usage is {} Gb'.format(usage/1000))","execution_count":null,"outputs":[]},{"metadata":{"_uuid":"fa769e6adb0296a6f18916a296362d51a42bfe12"},"cell_type":"markdown","source":"### Which continent has the most visitors to GStore?"},{"metadata":{"scrolled":true,"trusted":true,"_uuid":"84462aa2e831afc78d7d0821e9605e2d519bb5f9"},"cell_type":"code","source":"# Extract continent from geoNetwork field\nfield_name = 'geoNetwork' \nextract_field = 'continent'\nnew_field_name = 'continent'\n\njson_read(df_geo, field_name, extract_field, new_field_name).head()","execution_count":null,"outputs":[]},{"metadata":{"trusted":true,"_uuid":"5238f4544876a71fa6e0ce48a2859ee9b21df0bc"},"cell_type":"code","source":"df_geo.groupby('continent').size().plot.bar(color=color, rot = 0)\nplt.ylabel('visit counts')","execution_count":null,"outputs":[]},{"metadata":{"_uuid":"c84e216c54587b3e33a61c694e8a1e91f0b63e9d"},"cell_type":"markdown","source":"From above bar plot, we can see that most of customers are from Americas. Then the second most is from Asia. And visit counts from Europe is close to Asia. Hence, we can conclude that most of customers are from these three continents, Americas, Asia, and Europe."},{"metadata":{"_uuid":"4d2fcb8df8c878fbccc2586e8d425b3c39755fe4"},"cell_type":"markdown","source":"### Which continent has the highest purchasing rate?"},{"metadata":{"trusted":true,"_uuid":"96cb0b879470b122a2a54e1b79a781f5335118d1"},"cell_type":"code","source":"df_geo.groupby('continent')['Buy'].mean().plot.bar(color=color, rot = 0)\nplt.ylabel('Purchasing Rate')","execution_count":null,"outputs":[]},{"metadata":{"_uuid":"5df7e03317b5455c6f21bc3235fb844727a89ade"},"cell_type":"markdown","source":"From above bar plot, we can see that the highest purchasing rate is in Americas. **This impliest that customers from Americas have higher possibility to buy things in GStore.** "},{"metadata":{"scrolled":true,"trusted":true,"_uuid":"5fa7ed8ccf01cd00ea3294d5dfd06934ffe5dac9"},"cell_type":"code","source":"# Extract country from geoNetwork field\nfield_name = 'geoNetwork' \nextract_field = 'country'\nnew_field_name = 'country'\n\njson_read(df_geo, field_name, extract_field, new_field_name).head()","execution_count":null,"outputs":[]},{"metadata":{"_uuid":"22b853e2c259954920924c40346959cdc40b3b79"},"cell_type":"markdown","source":"### Which country in Americas has the highest purchasing rate?"},{"metadata":{"trusted":true,"_uuid":"a96a43875f0a614c81440cdd25139726505064cd"},"cell_type":"code","source":"df_americas = df_geo[df_geo['continent'] == 'Americas'] \ndf_americas.groupby('country')['Buy'].mean().sort_values(ascending=False).head(10)","execution_count":null,"outputs":[]},{"metadata":{"_uuid":"75b37eff31b6c1f523db162cacba1e3deaf70673"},"cell_type":"markdown","source":"Visitors from St. Lucia in Americas are the most probable to purchase goods in Gstore. But is it reasonable? Let's take a look at how many visits are from St. Lucia below."},{"metadata":{"trusted":true,"_uuid":"7efd1a47ce67f885a7a1585ee4eadf83fea5bd40"},"cell_type":"code","source":"# How many visitors are from Anguilla?\nlen(df_americas[df_americas['country']=='St. Lucia'])","execution_count":null,"outputs":[]},{"metadata":{"_uuid":"6da611874dc6192d715a501df6355626c45c5b34"},"cell_type":"markdown","source":"It is clear that we do not have enough data from this country so we cannot conclude that visitors from Anguilla in Americas are the most probable to purchase goods in Gstore. Let's examine other countries, St. Lucia, Guadeloupe, Curaçao, and United States, with high purchasing rate in the following:"},{"metadata":{"trusted":true,"_uuid":"8afe30d939271bb43126691522ab753d90e6389e"},"cell_type":"code","source":"df_americas.groupby('country').size().sort_values(ascending=False).head(25)","execution_count":null,"outputs":[]},{"metadata":{"_uuid":"97b592f71c17e77920e904fd136b25d5d2edeb3c"},"cell_type":"markdown","source":"As we can see in above results, there are less than 55 visits in St. Lucia, Guadeloupe, Curaçao so we cannot conclude whether customers from these countries are more willing to buy in GStore. In contrast, **United States has the highest visits and higher purchasing rate.** This is an important feature we can consider!"},{"metadata":{"_uuid":"76f76d24a25a5c5f83f7fb8c7f589ab4857d1573"},"cell_type":"markdown","source":"### Is continent related to revenue?"},{"metadata":{"trusted":true,"_uuid":"38f0f86f6c4cfa1820fe819b7971b8d5bb40b4de"},"cell_type":"code","source":"df_geo_Buy = df_geo[df_geo['Buy']==1]\ndf_geo_Buy.head()","execution_count":null,"outputs":[]},{"metadata":{"trusted":true,"_uuid":"a05a06c765d48e1aecae24f4526b89d30d873c9d"},"cell_type":"code","source":"# Transform revenue by log function\ndf_geo_Buy['log(revenue)'] = df_geo_Buy['revenue'].apply(np.log)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true,"_uuid":"3b99fd7ac2ee5e1d0066f8e0a34f5f64bcfd15b0"},"cell_type":"code","source":"sns.boxplot(x='continent', y='log(revenue)', data=df_geo_Buy)","execution_count":null,"outputs":[]},{"metadata":{"_uuid":"4b00b12573c0c9b9904cae2aa2bbf7a51bdb90d6"},"cell_type":"markdown","source":"As we can see in above box plot, the median of `log(revenue)` for each continent is close, except for Africa. But the distribution of `log(revenue)` for Africa is highly right-skewed. Let's take a look at how many visits with purchasing in Africa in the following:"},{"metadata":{"trusted":true,"_uuid":"90afd2efd3d3025f682da910858bf6af0dda01ff"},"cell_type":"code","source":"len(df_geo_Buy[df_geo_Buy['continent']== 'Africa'])","execution_count":null,"outputs":[]},{"metadata":{"_uuid":"a973cd1bebd1b982fb64488a5742a7e9cbf1beaa"},"cell_type":"markdown","source":"The samples in Africa are very less so the results cannot really show that customers from Africa contribute higher revenues compared with customers from other continents. Therefore, when we predict revenues, we will not include `continent` feature."},{"metadata":{"trusted":true,"_uuid":"96974d8a342eb40038f53c8ba90f0f5ff826ac80"},"cell_type":"code","source":"del df_americas, df_geo, df_geo_Buy","execution_count":null,"outputs":[]},{"metadata":{"_uuid":"f8f56151f017a3fdf33e6f49aeb111eae81af67a"},"cell_type":"markdown","source":"## 2.5 social Engagement Type\n\nIn this section, we study the relation between purchasing rate and `socialEngagementType`. Then we study whether `revenue` is related to `socialEngagementType`."},{"metadata":{"_uuid":"902a1d709db001fab7469d3c3aef19eedd30edc7"},"cell_type":"markdown","source":"### Which social Engagement Type has more visits?"},{"metadata":{"trusted":true,"_uuid":"844de1786bff53cb5946835abda07db0ca706a8b"},"cell_type":"code","source":"# using an html hex string for color\ncolor = '#0099ff'\ndf_train.groupby(['socialEngagementType']).size().plot.bar(rot = 0, color=color)\nplt.ylabel('Visits')","execution_count":null,"outputs":[]},{"metadata":{"trusted":true,"_uuid":"dc54ce7bcb031a7d146a9c12025debb805277543"},"cell_type":"code","source":"df_train['socialEngagementType'].count()","execution_count":null,"outputs":[]},{"metadata":{"_uuid":"5589505ad58e499eefeda04b522e368ddd8c75f0"},"cell_type":"markdown","source":"In this feature, samples in training set are all in the same category so it is not useful to include this feature into our model."},{"metadata":{"_uuid":"9fbf5c4209d3458e0a50d19ce0bf5c2cbeb09dc1"},"cell_type":"markdown","source":"## 2.6 Traffic Source"},{"metadata":{"trusted":true,"_uuid":"1dfb78530636083c22962ddb1a1166ff15dabb3e"},"cell_type":"code","source":"df_train.loc[101,'trafficSource']","execution_count":null,"outputs":[]},{"metadata":{"trusted":true,"_uuid":"f69515b899d4117bd51a800818ba55fb4fa674dd"},"cell_type":"code","source":"df_train.loc[102,'trafficSource']","execution_count":null,"outputs":[]},{"metadata":{"trusted":true,"_uuid":"39e00ad215625a627d4a1da710e5e50cf9a355f9"},"cell_type":"code","source":"df_train.loc[103,'trafficSource']","execution_count":null,"outputs":[]},{"metadata":{"_uuid":"50e2eb4db19a2f64b851f612ce82dc30bdf2dbb4"},"cell_type":"markdown","source":"In above three instances, except for those not available info, the most useful feature is `medium` but it's already extracted in `channelGrouping`."},{"metadata":{"_uuid":"f2ec37bc12756ef274ff214e519f88ca0dcfe5c7"},"cell_type":"markdown","source":"## 2.7 totals\n\nExcept for `transactionRevenue`, there are other useful features stored in json format in `totals` field like `visits`, `hits`, and `pageviews`."},{"metadata":{"trusted":true,"_uuid":"867decfdd2ad69649606730eaf9e30d31e9d0df6"},"cell_type":"code","source":"df_train.loc[80,'totals']","execution_count":null,"outputs":[]},{"metadata":{"trusted":true,"_uuid":"48721349358617376ebb5cc7f5df8af1a8745052"},"cell_type":"code","source":"df_train.loc[1001,'totals']","execution_count":null,"outputs":[]},{"metadata":{"trusted":true,"_uuid":"98ac7cd0a0fa335b38d1f28037e5d374b240bf13"},"cell_type":"code","source":"df_train.loc[302,'totals']","execution_count":null,"outputs":[]},{"metadata":{"trusted":true,"_uuid":"5001a7840135be984a2f7a863048cacdab416a62"},"cell_type":"code","source":"df_totals = df_train[['totals', 'revenue', 'Buy']]\ndf_totals.head()","execution_count":null,"outputs":[]},{"metadata":{"trusted":true,"_uuid":"f9db8298ff50a6207579e9b76ea787c2894a38c1"},"cell_type":"code","source":"# View momory usage including objects\ndf_totals.info(memory_usage='deep')","execution_count":null,"outputs":[]},{"metadata":{"_uuid":"cfdff6058321846b1a50a8fb5660d3bdfcc6ec09"},"cell_type":"markdown","source":"### visits feature in totals"},{"metadata":{"trusted":true,"_uuid":"288f643f7a25a62c87d5308bef01a281d8ec3600"},"cell_type":"code","source":"# Extract visits from totals field\nfield_name = 'totals' \nextract_field = 'visits'\nnew_field_name = 'visits'\n\njson_read(df_totals, field_name, extract_field, new_field_name).head()","execution_count":null,"outputs":[]},{"metadata":{"trusted":true,"_uuid":"97f8aad53c3105914c602df54ea409bd56a16098"},"cell_type":"code","source":"df_totals.groupby('visits').size()","execution_count":null,"outputs":[]},{"metadata":{"_uuid":"bdb70d56f8767d83c11d86a9586be9c6507d26f9"},"cell_type":"markdown","source":"From above result, the values in visits attribute are all 1 so this feature cannot offer any insight we need. But **it implies that in this data set, each instance is related to distinct customer visiting GStore**. "},{"metadata":{"trusted":true,"_uuid":"73a5388836fa99eb6242a7eee6ac913f6d25a40f"},"cell_type":"code","source":"# Drop unuseful feature to release memory\ndf_totals.drop(columns='visits', inplace=True)\ndf_totals.head()","execution_count":null,"outputs":[]},{"metadata":{"_uuid":"554d7aaebc5d299b2e8fccfdf145c0fa392ff5f9"},"cell_type":"markdown","source":"### hits and pageviews features in totals"},{"metadata":{"trusted":true,"_uuid":"98d4f49ec251f462a6bc22d9ec29853202f8ff06"},"cell_type":"code","source":"# Extract hits from totals field\nfield_name = 'totals' \nextract_field = 'hits'\nnew_field_name = 'hits'\n\njson_read(df_totals, field_name, extract_field, new_field_name).head()","execution_count":null,"outputs":[]},{"metadata":{"trusted":true,"_uuid":"755536523491be9cad9ffd362a6623e470599ece"},"cell_type":"code","source":"df_totals.groupby('hits').size().head()","execution_count":null,"outputs":[]},{"metadata":{"trusted":true,"_uuid":"ef2726554c1ef771113552521ed04b4c5a6bb1e4"},"cell_type":"code","source":"# Extract pageviews from totals field\nfield_name = 'totals' \nextract_field = 'pageviews'\nnew_field_name = 'pageviews'\n\njson_read(df_totals, field_name, extract_field, new_field_name).head()","execution_count":null,"outputs":[]},{"metadata":{"trusted":true,"_uuid":"bfcf59351794edb9d00318e5ac842b68d9689d9a"},"cell_type":"code","source":"df_totals.groupby('pageviews').size().head()","execution_count":null,"outputs":[]},{"metadata":{"trusted":true,"_uuid":"57bac35d2a99dbdc260085549949315556b689a1"},"cell_type":"code","source":"# Check data types for values in each column\ndf_totals.dtypes","execution_count":null,"outputs":[]},{"metadata":{"trusted":true,"_uuid":"3eb5c02e8a52ee222559a011bada5f2449a324d5"},"cell_type":"code","source":"# Convert desired columns to numeric type\ndf_totals[['hits', 'pageviews']] = df_totals[['hits', 'pageviews']].apply(pd.to_numeric) \ndf_totals.dtypes","execution_count":null,"outputs":[]},{"metadata":{"_uuid":"acbfaabc458c3a777069629ea5e33d732f7ffd7f"},"cell_type":"markdown","source":"### Take a look at correlations between revenue, Buy, hits, and pageviews"},{"metadata":{"trusted":true,"_uuid":"c2882a699880df42c6de2cd5dbad68752727d621"},"cell_type":"code","source":"temp_df = df_totals[['hits','pageviews', 'Buy','revenue']]\n\n# Calculate correlations\ncorr = temp_df.corr()\n\n# Heatmap\nsns.heatmap(corr)","execution_count":null,"outputs":[]},{"metadata":{"_uuid":"8db780d9b4f421e896bc2a39cc3334997875d756"},"cell_type":"markdown","source":"From above heatmap, we can see that `hits` and `pageviews` have very weak relation to `revenue`. However, `hits` and `pageviews` have very highly positive correlations with `Buy`. "},{"metadata":{"trusted":true,"_uuid":"cee2adb75177aba61ac2f4d4bfba23d1e2718277"},"cell_type":"code","source":"","execution_count":null,"outputs":[]}],"metadata":{"kernelspec":{"display_name":"Python 3","language":"python","name":"python3"},"language_info":{"name":"python","version":"3.6.6","mimetype":"text/x-python","codemirror_mode":{"name":"ipython","version":3},"pygments_lexer":"ipython3","nbconvert_exporter":"python","file_extension":".py"}},"nbformat":4,"nbformat_minor":1}