Exploratory analysis

Initial exploratory analysis to get a feel of the data. Two files:

  • Streams: 60 Tracks and their stream counts across different categories for specific dates
  • Popularity: Tracks and their popularity for specific dates

Popularity

In [30]:
%matplotlib inline
%pylab inline
pylab.rcParams['figure.figsize'] = (12, 7)
import pandas as pd
import os
import numpy as np
import matplotlib.pyplot as plt

from plotnine import *

pd.options.display.max_columns=500

data_dir = '../data/archive_as_of_friday_20_july/'
Populating the interactive namespace from numpy and matplotlib
/home/paperspace/anaconda3/envs/whitelist/lib/python3.6/site-packages/IPython/core/magics/pylab.py:160: UserWarning: pylab import has clobbered these variables: ['arrow', 'annotate', 'ylim', 'xlim']
`%matplotlib` prevents importing * from pylab and numpy
  "\n`%matplotlib` prevents importing * from pylab and numpy"
In [2]:
pop = pd.read_feather(os.path.join(data_dir, 'popularity.feather'))
pop.date = pd.to_datetime(pop.date)
In [3]:
pop.head()
Out[3]:
track_id date popularity
0 00kzys67XYXiB31cSP5jfo 2017-02-04 0
1 00kzys67XYXiB31cSP5jfo 2017-02-05 1
2 00kzys67XYXiB31cSP5jfo 2017-02-06 36
3 00kzys67XYXiB31cSP5jfo 2017-02-07 38
4 00kzys67XYXiB31cSP5jfo 2017-02-08 40
In [4]:
number_of_rows = len(pop)
number_of_rows
Out[4]:
50515
In [5]:
number_of_tracks = len(pop.track_id.unique())
number_of_tracks
Out[5]:
267
In [6]:
# number of popularities per track
pop.groupby('track_id').size().agg([np.min, np.mean, np.median, np.std, np.max])
Out[6]:
amin       79.000000
mean      189.194757
median    193.000000
std        60.905877
amax      439.000000
dtype: float64
In [7]:
date_ranges = pop['date'].apply([np.min, np.max])
date_ranges
Out[7]:
amin   2016-10-30
amax   2018-05-17
Name: date, dtype: datetime64[ns]
In [8]:
counts_by_date =  pop.groupby('date').size().to_frame('count').reset_index()
counts_by_date.head()
Out[8]:
date count
0 2016-10-30 1
1 2016-11-01 1
2 2016-11-02 1
3 2016-11-03 1
4 2016-11-04 3
In [9]:
#plot data
counts_by_date.plot( x = 'date', y = 'count')
Out[9]:
<matplotlib.axes._subplots.AxesSubplot at 0x7fc24eb3bd30>
In [10]:
first_date_per_track = pop.groupby('track_id').first()
first_date_per_track.head()
Out[10]:
date popularity
track_id
00kzys67XYXiB31cSP5jfo 2017-02-04 0
00mc2RHScEYMEFlc7FRGaK 2017-10-21 0
04HzRAn3BJaIvmhpvc1GVT 2017-01-20 0
04qrVtScdD4IBGSL5q6yEv 2017-11-25 0
09aaq7feVx9Jykdw0f00QU 2017-05-06 0
In [11]:
first_date_per_track.groupby('date').size().plot()
Out[11]:
<matplotlib.axes._subplots.AxesSubplot at 0x7fc24edae128>
In [12]:
pop.describe()
Out[12]:
popularity
count 50515.000000
mean 43.922775
std 15.320415
min 0.000000
25% 36.000000
50% 45.000000
75% 55.000000
max 82.000000
In [13]:
pop.groupby('date').agg([np.median]).plot()
Out[13]:
<matplotlib.axes._subplots.AxesSubplot at 0x7fc24ecaf320>
In [14]:
pop.groupby('date').agg([np.mean]).plot()
Out[14]:
<matplotlib.axes._subplots.AxesSubplot at 0x7fc24ed27f98>
In [15]:
aa = pop.groupby('track_id').diff().rename(columns={'date':'date_change', 'day_index':'day_index_change', 'popularity':'popularity_change'})
pop = pd.concat([pop,aa], axis =1)

pop.date_change.fillna(0, inplace = True)
pop['date_change_int'] = pop['date_change'].dt.days
pop['day_index'] = pop.groupby('track_id')['date_change_int'].cumsum()


#pop['day_index_change']=(pop.actual_day_index - pop.day_index)
In [16]:
#pop['day_index'] = pop.groupby('track_id').cumcount()
pop.head()
Out[16]:
track_id date popularity date_change popularity_change date_change_int day_index
0 00kzys67XYXiB31cSP5jfo 2017-02-04 0 0 days NaN 0 0
1 00kzys67XYXiB31cSP5jfo 2017-02-05 1 1 days 1.0 1 1
2 00kzys67XYXiB31cSP5jfo 2017-02-06 36 1 days 35.0 1 2
3 00kzys67XYXiB31cSP5jfo 2017-02-07 38 1 days 2.0 1 3
4 00kzys67XYXiB31cSP5jfo 2017-02-08 40 1 days 2.0 1 4
In [17]:
popularity_by_day = pop.groupby('day_index')['popularity'].agg([np.min, np.median, np.mean, np.std, np.max, len])
popularity_by_day.head()
Out[17]:
amin median mean std amax len
day_index
0 0 0.0 2.220974 8.734592 53 267
1 0 0.0 7.212121 12.793493 56 264
2 0 25.5 23.526718 18.184802 57 262
3 0 41.0 37.877395 14.504651 63 261
4 0 44.0 42.125475 12.213567 66 263
In [18]:
plt.plot(popularity_by_day.index, popularity_by_day['median'])
plt.plot(popularity_by_day.index, popularity_by_day['mean'])
plt.plot(popularity_by_day.index, popularity_by_day['mean'] + 2*popularity_by_day['std'])
plt.plot(popularity_by_day.index, popularity_by_day['mean'] - 2*popularity_by_day['std'])
Out[18]:
[<matplotlib.lines.Line2D at 0x7fc24eb85208>]
In [19]:
plt.plot(popularity_by_day.index, popularity_by_day['median'])
plt.plot(popularity_by_day.index, popularity_by_day['mean'])
plt.plot(popularity_by_day.index, popularity_by_day['mean'] + 2*popularity_by_day['std'])
plt.plot(popularity_by_day.index, popularity_by_day['mean'] - 2*popularity_by_day['std'])
plt.axvline(x = 5)

plt.xlim([-2, 40])
Out[19]:
(-2, 40)
In [20]:
import matplotlib as mp

def plotAllPops(pop, x_min = 0, x_max = 150, x = 'day_index'):
    cmap = mp.cm.autumn
    by_track_id = pop.groupby('track_id')
    cc = 0
    for name, group in by_track_id:
        plt.plot(group[x], group['popularity'], color=cmap(cc/number_of_tracks), alpha = 0.7)
        cc +=1

    plt.title('Popularity over time')
    plt.xlabel(x)
    plt.xlim(x_min, x_max)
    
plotAllPops(pop)
In [21]:
plotAllPops(pop, -1, 10)
In [22]:
plt.plot(popularity_by_day.index, popularity_by_day['std'])
plt.xlim([0,50])
plt.axvline(x = 5)
Out[22]:
<matplotlib.lines.Line2D at 0x7fc247f98d30>
In [23]:
plt.plot(popularity_by_day.index, popularity_by_day['len'])
Out[23]:
[<matplotlib.lines.Line2D at 0x7fc24eb7cd30>]
  • How do we know when a track is released? Is it at the first data point? ~Ask Joel~ ### Clean popularity -- remove jumps to 0
In [24]:
replacment_pop = -1*pop.loc[(pop['popularity'] == 0) & (pop['popularity_change'] < -1), 'popularity_change']

# CLEANING: Remove sharp drops to 0 that transition more than one step  
pop.loc[(pop['popularity'] == 0) & (pop['popularity_change'] < -1), 'popularity'] = replacment_pop

plotAllPops(pop)
In [25]:
pop['popularity_change'].fillna(0, inplace = True)
In [26]:
pop_change_by_day = pop.groupby('day_index')['popularity_change'].agg([np.mean, np.std, len]).reset_index()
pop_change_by_day.head()
Out[26]:
day_index mean std len
0 0 0.000000 0.000000 267.0
1 1 5.261364 9.672777 264.0
2 2 16.030534 15.111813 262.0
3 3 14.616858 16.717067 261.0
4 4 4.608365 9.629523 263.0
In [34]:
plt.plot(pop_change_by_day.day_index, pop_change_by_day['mean'])
plt.axvline(5)
xlim([0,50])
Out[34]:
<plotnine.scales.limits.xlim at 0x7fc24c0f5630>
In [37]:
ggplot(pop_change_by_day, aes('day_index', 'mean')) + geom_line() + coord_cartesian(xlim=[0,50])
/home/paperspace/anaconda3/envs/whitelist/lib/python3.6/site-packages/pandas/core/generic.py:4388: FutureWarning: Attribute 'is_copy' is deprecated and will be removed in a future version.
  object.__getattribute__(self, name)
/home/paperspace/anaconda3/envs/whitelist/lib/python3.6/site-packages/pandas/core/generic.py:4389: FutureWarning: Attribute 'is_copy' is deprecated and will be removed in a future version.
  return object.__setattr__(self, name, value)
Out[37]:
<ggplot: (-9223363257325013346)>
In [38]:
plt.plot(pop_change_by_day.day_index, pop_change_by_day['std'])
plt.axvline(5)
xlim([0,50])
Out[38]:
<plotnine.scales.limits.xlim at 0x7fc24dacbc50>

TODO: Calculate days after release for popularity

In [39]:
release_dates = pd.read_feather(f'{data_dir}/track_release_dates.feather')
release_dates.derived_release_date = pd.to_datetime(release_dates.derived_release_date)
release_dates.set_index('track_id', inplace = True)
pop = pop.join(release_dates, on = 'track_id', how='left')
In [40]:
pop['days_after_release'] = ((pop['date']  - pop['derived_release_date'])/pd.Timedelta('1 day')).astype(int)
pop.head()
Out[40]:
track_id date popularity date_change popularity_change date_change_int day_index derived_release_date days_after_release
0 00kzys67XYXiB31cSP5jfo 2017-02-04 0.0 0 days 0.0 0 0 2017-02-03 1
1 00kzys67XYXiB31cSP5jfo 2017-02-05 1.0 1 days 1.0 1 1 2017-02-03 2
2 00kzys67XYXiB31cSP5jfo 2017-02-06 36.0 1 days 35.0 1 2 2017-02-03 3
3 00kzys67XYXiB31cSP5jfo 2017-02-07 38.0 1 days 2.0 1 3 2017-02-03 4
4 00kzys67XYXiB31cSP5jfo 2017-02-08 40.0 1 days 2.0 1 4 2017-02-03 5
In [41]:
plotAllPops(pop, 0, 10, 'days_after_release')
In [42]:
plotAllPops(pop, x='days_after_release')
In [43]:
plotAllPops(pop)
  1. It looks like the using the days after release does not help with the inital days. Maybe the calculations are staggered somehow - didn't I read that somwehre?
  2. There is a vertical line that is fixed by using the relase date, so it might just edge the previous verison.
In [48]:
#pop.to_feather('../data/archive_as_of_friday_20_july/pop_60_cleaned_with_days_after_release.feather')
pop.drop('date_change', axis = 1).to_feather('../data/archive_as_of_friday_20_july/pop_60_cleaned_with_days_after_release.feather')

Streams

This file is from Sony and it's the historical stream counts. The data is only available for artist that were signed.

In [71]:
streams = pd.read_feather(f'{data_dir}/streams_60.feather')
In [72]:
streams.head()
Out[72]:
track_id date region all_streams source_album_page_streams source_artist_page_streams source_chart_streams source_collection_streams source_daily_mix_streams source_discover_weekly_streams source_other_streams source_playlist_streams source_radio_streams soruce_release_radar_streams source_search_streams sony_playlist_streams spotify_playlist_streams otehr_playlist_streams shuffle_streams
0 00kzys67XYXiB31cSP5jfo 2017-02-02 Worldwide 30 6 7 0 1 0 0 2 14 0 0 0 NaN 9.0 NaN 0
1 00kzys67XYXiB31cSP5jfo 2017-02-03 Worldwide 17136 1617 365 0 865 0 0 4875 9342 0 0 72 57.0 8741.0 32.0 0
2 00kzys67XYXiB31cSP5jfo 2017-02-04 Worldwide 9116 911 540 0 1733 0 0 1047 4809 0 0 76 81.0 4111.0 29.0 0
3 00kzys67XYXiB31cSP5jfo 2017-02-05 Worldwide 7405 474 610 0 1574 0 0 791 3900 0 0 56 53.0 3400.0 84.0 0
4 00kzys67XYXiB31cSP5jfo 2017-02-06 Worldwide 7868 301 763 0 1572 0 0 968 4198 0 0 66 82.0 3559.0 400.0 0
In [73]:
len(streams)
Out[73]:
19221
In [74]:
len(streams['track_id'].unique())
Out[74]:
61
In [75]:
streams['date'].apply([np.min, np.max])
Out[75]:
amin   2016-11-03
amax   2018-05-16
Name: date, dtype: datetime64[ns]
In [76]:
streams.groupby('region').size()
Out[76]:
region
Worldwide    19221
dtype: int64
In [77]:
unique_tracks = streams['track_id'].unique()
#pop.set_index('track_id', inplace = True)
track_streams_with_pop = streams.join(release_dates, on = 'track_id')
tt = track_streams_with_pop.groupby('track_id').agg({'date':np.min, 'derived_release_date': np.min})
tt['data_on_days_after_release'] = tt['date'] - tt['derived_release_date']
tt.describe()
Out[77]:
data_on_days_after_release
count 61
mean -3 days +08:15:44.262295
std 41 days 09:30:15.122361
min -267 days +00:00:00
25% -1 days +00:00:00
50% -1 days +00:00:00
75% -1 days +00:00:00
max 123 days 00:00:00
In [78]:
tt[tt['data_on_days_after_release'] > pd.Timedelta('0d')]
Out[78]:
date derived_release_date data_on_days_after_release
track_id
0uIBHtYDehJwG78e0yYjXd 2017-11-01 2017-07-12 112 days
184HI8TB2GGFMkgOQvVwYW 2017-05-15 2017-05-12 3 days
1Ba0ucfKPaRzS3AKvX3X3r 2017-05-15 2017-01-12 123 days
In [79]:
tt[tt['data_on_days_after_release'] < pd.Timedelta('-1d')]
Out[79]:
date derived_release_date data_on_days_after_release
track_id
0U1pfb8oRmhHazPuIOndaM 2016-11-03 2016-11-11 -8 days
0bPSRn4crnh5f1JhELPlyL 2016-12-01 2017-02-03 -64 days
1ekNZULmcBHp3WRNKft7ou 2017-11-09 2017-11-17 -8 days
44mJEP4jnRy7crjl5vtJLk 2017-05-18 2018-02-09 -267 days
In [95]:
#streams[streams['track_id'] == '44mJEP4jnRy7crjl5vtJLk']
In [96]:
import json
from pprint import pprint

with open(f'{data_dir}/sony_tracks_data.json') as track_data:
    t_d = json.load(track_data)
    
this_stream = [x for x in t_d['results'] if x['track_id'] == '44mJEP4jnRy7crjl5vtJLk']
#pprint(this_stream)

Train/ test set

THIS DOES NOT APPLY RIGHT NOW. --In order to have a minimal test set, if the 60 streams are the full extent of what we can get, set aside 10 tracks as test set. Because of the nature of predictions in time, let's take the last 10 in terms of release date.--

I'm hopeful this won't be needed, ie, we can use the full sample for testing.

In [83]:
train_streams = streams
In [84]:
train_streams.head()
Out[84]:
track_id date region all_streams source_album_page_streams source_artist_page_streams source_chart_streams source_collection_streams source_daily_mix_streams source_discover_weekly_streams source_other_streams source_playlist_streams source_radio_streams soruce_release_radar_streams source_search_streams sony_playlist_streams spotify_playlist_streams otehr_playlist_streams shuffle_streams
0 00kzys67XYXiB31cSP5jfo 2017-02-02 Worldwide 30 6 7 0 1 0 0 2 14 0 0 0 NaN 9.0 NaN 0
1 00kzys67XYXiB31cSP5jfo 2017-02-03 Worldwide 17136 1617 365 0 865 0 0 4875 9342 0 0 72 57.0 8741.0 32.0 0
2 00kzys67XYXiB31cSP5jfo 2017-02-04 Worldwide 9116 911 540 0 1733 0 0 1047 4809 0 0 76 81.0 4111.0 29.0 0
3 00kzys67XYXiB31cSP5jfo 2017-02-05 Worldwide 7405 474 610 0 1574 0 0 791 3900 0 0 56 53.0 3400.0 84.0 0
4 00kzys67XYXiB31cSP5jfo 2017-02-06 Worldwide 7868 301 763 0 1572 0 0 968 4198 0 0 66 82.0 3559.0 400.0 0
In [89]:
train_streams.reset_index().to_feather(f'{data_dir}/train_streams.feather')

Training data EDA (Streams)

In [90]:
train_streams.head()
Out[90]:
date region all_streams source_album_page_streams source_artist_page_streams source_chart_streams source_collection_streams source_daily_mix_streams source_discover_weekly_streams source_other_streams source_playlist_streams source_radio_streams soruce_release_radar_streams source_search_streams sony_playlist_streams spotify_playlist_streams otehr_playlist_streams shuffle_streams derived_release_date
track_id
00kzys67XYXiB31cSP5jfo 2017-02-02 Worldwide 30 6 7 0 1 0 0 2 14 0 0 0 NaN 9.0 NaN 0 2017-02-03
00kzys67XYXiB31cSP5jfo 2017-02-03 Worldwide 17136 1617 365 0 865 0 0 4875 9342 0 0 72 57.0 8741.0 32.0 0 2017-02-03
00kzys67XYXiB31cSP5jfo 2017-02-04 Worldwide 9116 911 540 0 1733 0 0 1047 4809 0 0 76 81.0 4111.0 29.0 0 2017-02-03
00kzys67XYXiB31cSP5jfo 2017-02-05 Worldwide 7405 474 610 0 1574 0 0 791 3900 0 0 56 53.0 3400.0 84.0 0 2017-02-03
00kzys67XYXiB31cSP5jfo 2017-02-06 Worldwide 7868 301 763 0 1572 0 0 968 4198 0 0 66 82.0 3559.0 400.0 0 2017-02-03
In [91]:
train_streams.set_index('track_id', inplace =True)
---------------------------------------------------------------------------
KeyError                                  Traceback (most recent call last)
~/anaconda3/envs/whitelist/lib/python3.6/site-packages/pandas/core/indexes/base.py in get_loc(self, key, method, tolerance)
   3077             try:
-> 3078                 return self._engine.get_loc(key)
   3079             except KeyError:

pandas/_libs/index.pyx in pandas._libs.index.IndexEngine.get_loc()

pandas/_libs/index.pyx in pandas._libs.index.IndexEngine.get_loc()

pandas/_libs/hashtable_class_helper.pxi in pandas._libs.hashtable.PyObjectHashTable.get_item()

pandas/_libs/hashtable_class_helper.pxi in pandas._libs.hashtable.PyObjectHashTable.get_item()

KeyError: 'track_id'

During handling of the above exception, another exception occurred:

KeyError                                  Traceback (most recent call last)
<ipython-input-91-63ce51a7a022> in <module>()
----> 1 train_streams.set_index('track_id', inplace =True)

~/anaconda3/envs/whitelist/lib/python3.6/site-packages/pandas/core/frame.py in set_index(self, keys, drop, append, inplace, verify_integrity)
   3907                 names.append(None)
   3908             else:
-> 3909                 level = frame[col]._values
   3910                 names.append(col)
   3911                 if drop:

~/anaconda3/envs/whitelist/lib/python3.6/site-packages/pandas/core/frame.py in __getitem__(self, key)
   2686             return self._getitem_multilevel(key)
   2687         else:
-> 2688             return self._getitem_column(key)
   2689 
   2690     def _getitem_column(self, key):

~/anaconda3/envs/whitelist/lib/python3.6/site-packages/pandas/core/frame.py in _getitem_column(self, key)
   2693         # get column
   2694         if self.columns.is_unique:
-> 2695             return self._get_item_cache(key)
   2696 
   2697         # duplicate columns & possible reduce dimensionality

~/anaconda3/envs/whitelist/lib/python3.6/site-packages/pandas/core/generic.py in _get_item_cache(self, item)
   2487         res = cache.get(item)
   2488         if res is None:
-> 2489             values = self._data.get(item)
   2490             res = self._box_item_values(item, values)
   2491             cache[item] = res

~/anaconda3/envs/whitelist/lib/python3.6/site-packages/pandas/core/internals.py in get(self, item, fastpath)
   4113 
   4114             if not isna(item):
-> 4115                 loc = self.items.get_loc(item)
   4116             else:
   4117                 indexer = np.arange(len(self.items))[isna(self.items)]

~/anaconda3/envs/whitelist/lib/python3.6/site-packages/pandas/core/indexes/base.py in get_loc(self, key, method, tolerance)
   3078                 return self._engine.get_loc(key)
   3079             except KeyError:
-> 3080                 return self._engine.get_loc(self._maybe_cast_indexer(key))
   3081 
   3082         indexer = self.get_indexer([key], method=method, tolerance=tolerance)

pandas/_libs/index.pyx in pandas._libs.index.IndexEngine.get_loc()

pandas/_libs/index.pyx in pandas._libs.index.IndexEngine.get_loc()

pandas/_libs/hashtable_class_helper.pxi in pandas._libs.hashtable.PyObjectHashTable.get_item()

pandas/_libs/hashtable_class_helper.pxi in pandas._libs.hashtable.PyObjectHashTable.get_item()

KeyError: 'track_id'
In [92]:
train_streams = train_streams.join(release_dates)
---------------------------------------------------------------------------
ValueError                                Traceback (most recent call last)
<ipython-input-92-e035625f4534> in <module>()
----> 1 train_streams = train_streams.join(release_dates)

~/anaconda3/envs/whitelist/lib/python3.6/site-packages/pandas/core/frame.py in join(self, other, on, how, lsuffix, rsuffix, sort)
   6334         # For SparseDataFrame's benefit
   6335         return self._join_compat(other, on=on, how=how, lsuffix=lsuffix,
-> 6336                                  rsuffix=rsuffix, sort=sort)
   6337 
   6338     def _join_compat(self, other, on=None, how='left', lsuffix='', rsuffix='',

~/anaconda3/envs/whitelist/lib/python3.6/site-packages/pandas/core/frame.py in _join_compat(self, other, on, how, lsuffix, rsuffix, sort)
   6349             return merge(self, other, left_on=on, how=how,
   6350                          left_index=on is None, right_index=True,
-> 6351                          suffixes=(lsuffix, rsuffix), sort=sort)
   6352         else:
   6353             if on is not None:

~/anaconda3/envs/whitelist/lib/python3.6/site-packages/pandas/core/reshape/merge.py in merge(left, right, how, on, left_on, right_on, left_index, right_index, sort, suffixes, copy, indicator, validate)
     60                          copy=copy, indicator=indicator,
     61                          validate=validate)
---> 62     return op.get_result()
     63 
     64 

~/anaconda3/envs/whitelist/lib/python3.6/site-packages/pandas/core/reshape/merge.py in get_result(self)
    572 
    573         llabels, rlabels = items_overlap_with_suffix(ldata.items, lsuf,
--> 574                                                      rdata.items, rsuf)
    575 
    576         lindexers = {1: left_indexer} if left_indexer is not None else {}

~/anaconda3/envs/whitelist/lib/python3.6/site-packages/pandas/core/internals.py in items_overlap_with_suffix(left, lsuffix, right, rsuffix)
   5242         if not lsuffix and not rsuffix:
   5243             raise ValueError('columns overlap but no suffix specified: '
-> 5244                              '{rename}'.format(rename=to_rename))
   5245 
   5246         def lrenamer(x):

ValueError: columns overlap but no suffix specified: Index(['derived_release_date'], dtype='object')
In [94]:
train_streams['days_from_release']=train_streams['date']-train_streams['derived_release_date']
train_streams['all_streams_cumsum'] = train_streams.groupby('track_id')['all_streams'].cumsum()
train_streams['days_from_release'] = (train_streams['days_from_release']/ np.timedelta64(1, 'D')).astype(int)
train_streams.reset_index().to_feather(f'{data_dir}/train_streams_with_release.feather')

Visual EDA for Streams

In [129]:
def plotAllStreams(pop, x_min = 0, x_max = 150, x = 'days_from_release', y = 'all_streams_cumsum', log = True):
    cmap = mp.cm.autumn
    by_track_id = pop.groupby('track_id')
    number_of_tracks = len(by_track_id)
    cc = 0
    for name, group in by_track_id:
        y_data = np.log(group[y]) if log else group[y]
        plt.plot(group[x], y_data, color=cmap(cc/number_of_tracks), alpha = 0.7)
        cc +=1

    title = 'Log Stream count over time' if log else 'Stream count over time'
    plt.title(title)
    plt.xlabel(x)
    plt.xlim(x_min, x_max)
    
In [130]:
plotAllStreams(train_streams)
In [132]:
plotAllStreams(train_streams, x_max = 10, x_min = -2)
In [216]:
plotAllStreams(train_streams, x_max = 40, x_min = -2, y = 'all_streams', log = False)
In [161]:
ts = train_streams.copy().reset_index()
streams_day_1 = ts[ts['days_from_release'] == 0]
pop_5 = pop[pop['days_after_release'] == 5]

streams_day_1_pop5 = pd.merge(streams_day_1, pop_5, on = ['track_id'])
streams_day_1_pop5['log_streams_cumsum'] = np.log(streams_day_1_pop5['all_streams_cumsum'])
ss = streams_day_1_pop5[['log_streams_cumsum', 'popularity']]
pd.tools.plotting.scatter_matrix(ss)
/home/paperspace/anaconda3/envs/whitelist/lib/python3.6/site-packages/ipykernel_launcher.py:8: FutureWarning: 'pandas.tools.plotting.scatter_matrix' is deprecated, import 'pandas.plotting.scatter_matrix' instead.
  
Out[161]:
array([[<matplotlib.axes._subplots.AxesSubplot object at 0x7f8a9fe630f0>,
        <matplotlib.axes._subplots.AxesSubplot object at 0x7f8a9fe2a438>],
       [<matplotlib.axes._subplots.AxesSubplot object at 0x7f8aa0134c88>,
        <matplotlib.axes._subplots.AxesSubplot object at 0x7f8a9feaf278>]],
      dtype=object)
In [111]:
by_t = train_streams.groupby('track_id')
len(by_t)
Out[111]:
61
In [97]:
train_streams_100 = train_streams[train_streams['days_from_release'] == 100]
train_streams_100.tail()
Out[97]:
date region all_streams source_album_page_streams source_artist_page_streams source_chart_streams source_collection_streams source_daily_mix_streams source_discover_weekly_streams source_other_streams ... source_radio_streams soruce_release_radar_streams source_search_streams sony_playlist_streams spotify_playlist_streams otehr_playlist_streams shuffle_streams derived_release_date days_from_release all_streams_cumsum
track_id
1imBH4y5b85vL1mFDxBp9N 2018-01-21 Worldwide 34178 824 720 0 17294 0 0 8555 ... 0 0 690 234.0 4415.0 381.0 0 2017-10-13 100 3343036
1lLuhJSggOEPVfSgfa9r2n 2017-10-01 Worldwide 1453 25 70 0 615 0 0 171 ... 0 0 22 384.0 12.0 94.0 0 2017-06-23 100 1458398
1qLi7TR7RUGedFwV9b8sot 2017-03-05 Worldwide 2490 4 13 0 230 0 0 427 ... 0 0 25 NaN 1723.0 38.0 0 2016-11-25 100 468146
4n8df1lKaP8on42bdQJHcz 2017-03-12 Worldwide 12822 802 663 0 6633 0 0 1552 ... 0 0 165 317.0 1343.0 791.0 0 2016-12-02 100 2807637
5FqevirebpKNGF5FQk60ey 2018-04-22 Worldwide 22155 96 158 0 4225 718 91 3602 ... 1515 1 51 178.0 11315.0 15.0 11354 2018-01-12 100 1420185

5 rows × 21 columns

Note the below are firstly on 'all_streams' on day 100 - then further down there a similar analysis on all streams cumsum 100

In [98]:
train_streams_100.reset_index()['all_streams'].apply(np.log).plot.hist(bins = 50)
Out[98]:
<matplotlib.axes._subplots.AxesSubplot at 0x7f94e19b9be0>
In [99]:
pop.set_index('track_id', inplace = True)
In [100]:
pop_5 = pop[pop['day_index'] == 5]
pop_5 = pop_5[['popularity']]

train_streams_100 = train_streams_100[['all_streams']]
len(train_streams_100)
Out[100]:
54
In [101]:
df = pop_5.join(train_streams_100,how='inner')
pd.tools.plotting.scatter_matrix(df)
/home/sergiusz/anaconda3/envs/whitelist/lib/python3.6/site-packages/ipykernel/__main__.py:2: FutureWarning: 'pandas.tools.plotting.scatter_matrix' is deprecated, import 'pandas.plotting.scatter_matrix' instead.
  from ipykernel import kernelapp as app
Out[101]:
array([[<matplotlib.axes._subplots.AxesSubplot object at 0x7f94e19053c8>,
        <matplotlib.axes._subplots.AxesSubplot object at 0x7f94e1926be0>],
       [<matplotlib.axes._subplots.AxesSubplot object at 0x7f94e18d8278>,
        <matplotlib.axes._subplots.AxesSubplot object at 0x7f94e187d908>]],
      dtype=object)
In [102]:
df['log_all_streams'] = np.log(df['all_streams'])
In [103]:
pd.tools.plotting.scatter_matrix(df)
/home/sergiusz/anaconda3/envs/whitelist/lib/python3.6/site-packages/ipykernel/__main__.py:1: FutureWarning: 'pandas.tools.plotting.scatter_matrix' is deprecated, import 'pandas.plotting.scatter_matrix' instead.
  if __name__ == '__main__':
Out[103]:
array([[<matplotlib.axes._subplots.AxesSubplot object at 0x7f94e29bffd0>,
        <matplotlib.axes._subplots.AxesSubplot object at 0x7f94e29ddcf8>,
        <matplotlib.axes._subplots.AxesSubplot object at 0x7f94e281eb70>],
       [<matplotlib.axes._subplots.AxesSubplot object at 0x7f94e4411668>,
        <matplotlib.axes._subplots.AxesSubplot object at 0x7f94e27bb9b0>,
        <matplotlib.axes._subplots.AxesSubplot object at 0x7f94e27bb1d0>],
       [<matplotlib.axes._subplots.AxesSubplot object at 0x7f94e2741b38>,
        <matplotlib.axes._subplots.AxesSubplot object at 0x7f94e6a2b9e8>,
        <matplotlib.axes._subplots.AxesSubplot object at 0x7f94e275b5f8>]],
      dtype=object)

This treats all_streams_cumsum

In [104]:
train_streams_100 = train_streams[train_streams['days_from_release'] == 100]
train_streams_100.reset_index()['all_streams_cumsum'].apply(np.log).plot.hist(bins = 50)
Out[104]:
<matplotlib.axes._subplots.AxesSubplot at 0x7f94e28e1208>
In [105]:
train_streams_100 = train_streams_100[['all_streams_cumsum']]
In [106]:
df = pop_5.join(train_streams_100,how='inner')
#pd.tools.plotting.scatter_matrix(df)
In [107]:
df['log_all_streams_cumsum'] = np.log(df['all_streams_cumsum'])
In [108]:
pd.tools.plotting.scatter_matrix(df)
/home/sergiusz/anaconda3/envs/whitelist/lib/python3.6/site-packages/ipykernel/__main__.py:1: FutureWarning: 'pandas.tools.plotting.scatter_matrix' is deprecated, import 'pandas.plotting.scatter_matrix' instead.
  if __name__ == '__main__':
Out[108]:
array([[<matplotlib.axes._subplots.AxesSubplot object at 0x7f94e228ecf8>,
        <matplotlib.axes._subplots.AxesSubplot object at 0x7f94e23ca6a0>,
        <matplotlib.axes._subplots.AxesSubplot object at 0x7f94e1cb3d30>],
       [<matplotlib.axes._subplots.AxesSubplot object at 0x7f94e2a193c8>,
        <matplotlib.axes._subplots.AxesSubplot object at 0x7f94e1a10a58>,
        <matplotlib.axes._subplots.AxesSubplot object at 0x7f94e1a10a90>],
       [<matplotlib.axes._subplots.AxesSubplot object at 0x7f94e287a7b8>,
        <matplotlib.axes._subplots.AxesSubplot object at 0x7f94e1d0ae48>,
        <matplotlib.axes._subplots.AxesSubplot object at 0x7f94e2a6a518>]],
      dtype=object)
In [109]:
df.reset_index().to_feather('../data/basemodel_cumsum_outlier_removed.feather')

Use days after release instead

In [113]:
pop_5 = pop[pop['days_after_release'] == 5]
pop_5 = pop_5[['popularity']]
In [114]:
df = pop_5.join(train_streams_100,how='inner')
In [115]:
df['log_all_streams_cumsum'] = np.log(df['all_streams_cumsum'])
In [116]:
pd.tools.plotting.scatter_matrix(df)
/home/sergiusz/anaconda3/envs/whitelist/lib/python3.6/site-packages/ipykernel/__main__.py:1: FutureWarning: 'pandas.tools.plotting.scatter_matrix' is deprecated, import 'pandas.plotting.scatter_matrix' instead.
  if __name__ == '__main__':
Out[116]:
array([[<matplotlib.axes._subplots.AxesSubplot object at 0x7f94e1c4eac8>,
        <matplotlib.axes._subplots.AxesSubplot object at 0x7f94e1c4c748>,
        <matplotlib.axes._subplots.AxesSubplot object at 0x7f94e2490d68>],
       [<matplotlib.axes._subplots.AxesSubplot object at 0x7f94e1f96400>,
        <matplotlib.axes._subplots.AxesSubplot object at 0x7f94e1f9ca90>,
        <matplotlib.axes._subplots.AxesSubplot object at 0x7f94e1f9cac8>],
       [<matplotlib.axes._subplots.AxesSubplot object at 0x7f94e1a39320>,
        <matplotlib.axes._subplots.AxesSubplot object at 0x7f94e1ae0e80>,
        <matplotlib.axes._subplots.AxesSubplot object at 0x7f94e1ab6550>]],
      dtype=object)
In [117]:
df.reset_index().to_feather('../data/basemodel_cumsum_outlier_removed_days_after_release.feather')
In [118]:
df.head()
Out[118]:
popularity all_streams_cumsum log_all_streams_cumsum
track_id
00kzys67XYXiB31cSP5jfo 40.0 473673 13.068272
00mc2RHScEYMEFlc7FRGaK 26.0 93919 11.450188
04HzRAn3BJaIvmhpvc1GVT 51.0 1426517 14.170746
04qrVtScdD4IBGSL5q6yEv 47.0 2637515 14.785348
09aaq7feVx9Jykdw0f00QU 50.0 1371458 14.131385

Only use stream dates after pop 5!

In [198]:
start_of_stream_count = 5
end_of_stream_count = 100

#train_streams_100 = train_streams[train_streams['days_from_release'] == 100]
#train_streams_100.reset_index()['all_streams_cumsum'].apply(np.log).plot.hist(bins = 50)
pop_5_date = pop_5[['track_id','date']]
streams = pd.read_feather(f'{data_dir}/streams_60.feather')
tts = streams[['track_id', 'date', 'all_streams']]
rel = release_dates.reset_index()
tts = pd.merge(tts, rel, on='track_id')
tts['days_from_release']=tts['date']-tts['derived_release_date']
tts['days_from_release'] = (tts['days_from_release']/ np.timedelta64(1, 'D')).astype(int)
tts = tts[tts['days_from_release'] <= 100]
#tts['all_streams_cumsum'] = tts.groupby('track_id')['all_streams'].cumsum()
#tts.reset_index().to_feather(f'{data_dir}/tts_with_release.feather')
pop_5_date = pop_5_date.rename(columns = {'date':'pop_5_date'})
tts = pd.merge(tts, pop_5_date, on='track_id')
tts['after_pop_5'] = np.where(tts['date'] > tts['pop_5_date'], 'After pop 5', 'Before pop 5')
pp5 = tts.groupby(['track_id','after_pop_5'])[['all_streams']].sum()

pp5s = pp5.unstack()
pp5s.columns = pp5s.columns.droplevel(0)
pd.tools.plotting.scatter_matrix(np.log(pp5s))
/home/paperspace/anaconda3/envs/whitelist/lib/python3.6/site-packages/ipykernel_launcher.py:23: FutureWarning: 'pandas.tools.plotting.scatter_matrix' is deprecated, import 'pandas.plotting.scatter_matrix' instead.
Out[198]:
array([[<matplotlib.axes._subplots.AxesSubplot object at 0x7f8a9f241390>,
        <matplotlib.axes._subplots.AxesSubplot object at 0x7f8a9fb3d400>],
       [<matplotlib.axes._subplots.AxesSubplot object at 0x7f8a9eeb8908>,
        <matplotlib.axes._subplots.AxesSubplot object at 0x7f8a9f108f98>]],
      dtype=object)
In [202]:
np.corrcoef(pp5s['Before pop 5'], pp5s['After pop 5'])
Out[202]:
array([[1.        , 0.43685076],
       [0.43685076, 1.        ]])
In [204]:
from scipy.stats import spearmanr

spearmanr(pp5s['Before pop 5'], pp5s['After pop 5'])
Out[204]:
SpearmanrResult(correlation=0.7478236795964195, pvalue=1.5277134828401144e-11)
In [206]:
pp5s['fraction_after'] = pp5s['After pop 5'] / (pp5s['After pop 5'] + pp5s['Before pop 5'])
In [207]:
pp5s.describe()
Out[207]:
after_pop_5 After pop 5 Before pop 5 fraction_after
count 5.800000e+01 5.800000e+01 58.000000
mean 9.519293e+05 1.831646e+05 0.808186
std 1.034209e+06 3.069250e+05 0.160386
min 2.433000e+04 4.976000e+03 0.107328
25% 1.597602e+05 5.197700e+04 0.760046
50% 5.480110e+05 9.076400e+04 0.863774
75% 1.381447e+06 1.913250e+05 0.907748
max 4.786599e+06 2.164341e+06 0.955962
In [222]:
import statsmodels.api as sm

model = sm.OLS(np.log(pp5s['After pop 5']), np.log(pp5s['Before pop 5']) )
res = model.fit()
res.summary()
Out[222]:
OLS Regression Results
Dep. Variable: After pop 5 R-squared: 0.994
Model: OLS Adj. R-squared: 0.994
Method: Least Squares F-statistic: 8991.
Date: Sun, 19 Aug 2018 Prob (F-statistic): 2.02e-64
Time: 09:03:34 Log-Likelihood: -84.688
No. Observations: 58 AIC: 171.4
Df Residuals: 57 BIC: 173.4
Df Model: 1
Covariance Type: nonrobust
coef std err t P>|t| [0.025 0.975]
Before pop 5 1.1414 0.012 94.819 0.000 1.117 1.165
Omnibus: 17.505 Durbin-Watson: 2.198
Prob(Omnibus): 0.000 Jarque-Bera (JB): 24.701
Skew: -1.090 Prob(JB): 4.33e-06
Kurtosis: 5.338 Cond. No. 1.00


Warnings:
[1] Standard Errors assume that the covariance matrix of the errors is correctly specified.
In [220]:
pp = pd.merge(pp5s.reset_index(), pop_5[['track_id', 'popularity']], on = 'track_id')
pp.head()
Out[220]:
track_id After pop 5 Before pop 5 fraction_after popularity
0 00kzys67XYXiB31cSP5jfo 414835 58838 0.875784 40.0
1 00mc2RHScEYMEFlc7FRGaK 88943 4976 0.947018 26.0
2 04HzRAn3BJaIvmhpvc1GVT 1236386 190131 0.866717 51.0
3 04qrVtScdD4IBGSL5q6yEv 2506239 131276 0.950227 47.0
4 09aaq7feVx9Jykdw0f00QU 1202658 168800 0.876919 50.0
In [224]:
p_model = sm.OLS(np.log(pp['After pop 5']), pp['popularity'])
pres = p_model.fit()
pres.summary()
Out[224]:
OLS Regression Results
Dep. Variable: After pop 5 R-squared: 0.960
Model: OLS Adj. R-squared: 0.959
Method: Least Squares F-statistic: 1363.
Date: Sun, 19 Aug 2018 Prob (F-statistic): 1.71e-41
Time: 09:04:22 Log-Likelihood: -138.39
No. Observations: 58 AIC: 278.8
Df Residuals: 57 BIC: 280.8
Df Model: 1
Covariance Type: nonrobust
coef std err t P>|t| [0.025 0.975]
popularity 0.2846 0.008 36.918 0.000 0.269 0.300
Omnibus: 37.315 Durbin-Watson: 1.910
Prob(Omnibus): 0.000 Jarque-Bera (JB): 108.787
Skew: 1.864 Prob(JB): 2.38e-24
Kurtosis: 8.579 Cond. No. 1.00


Warnings:
[1] Standard Errors assume that the covariance matrix of the errors is correctly specified.