In [162]:
import os
import folium
import pandas as pd
import numpy as np
from folium import plugins
import branca.colormap as cm
from tqdm import tqdm
import matplotlib.pyplot as plt
import json

Obtain data


In [120]:
import requests
import time
response = requests.get("http://opendata.paris.fr/api/records/1.0/download/?dataset=stations-velib-disponibilites-en-temps-reel&facet=banking&facet=bonus&facet=status&facet=contract_name&rows=-1")
txt = response.text
f = open('velib.csv', 'w+')
f.write(txt)


Out[120]:
188975

In [121]:
velibs = pd.read_csv('velib.csv', sep=";")

In [122]:
velibs.head()


Out[122]:
number name address position banking bonus status contract_name bike_stands available_bike_stands available_bikes last_update
0 5106 05106 - CUJAS 22 RUE CUJAS - 75005 PARIS 48.8481913486, 2.34183165485 True False OPEN Paris 19 16 3 2017-09-14T00:08:26+00:00
1 13028 13028 - GOUTHIERE 12 RUE GOUTHIERE - 75013 PARIS 48.820507, 2.351342 True False CLOSED Paris 33 0 0 2017-09-14T12:20:31+00:00
2 14114 14114 - PLACE DE CATALOGNE 4 RUE ALAIN - 75014 PARIS 48.8372933094, 2.31748293064 True True OPEN Paris 43 0 0 2017-09-22T09:17:22+00:00
3 35007 35007 - DELESSERT (PANTIN) 1 RUE BENJAMIN DELESSERT - 93500 PANTIN 48.8939437132, 2.41802965969 True False OPEN Paris 21 4 16 2017-09-22T11:05:00+00:00
4 16001 16001 - AVENUE DES PORTUGAIS 2 AVENUE DES PORTUGAIS - 75016 PARIS 48.8712137122, 2.29369213365 True True OPEN Paris 26 10 16 2017-09-22T12:24:25+00:00

In [123]:
velibs.shape


Out[123]:
(1226, 12)

In [124]:
velibs = velibs[velibs.status == 'OPEN']

In [125]:
velibs['arron'] = velibs['address'].map(lambda x: int(x.split()[-2][3:]) if x.split()[-2][:2] == '75' else np.NaN)

In [126]:
velibs.shape


Out[126]:
(1186, 13)

In [127]:
velibs.head()


Out[127]:
number name address position banking bonus status contract_name bike_stands available_bike_stands available_bikes last_update arron
0 5106 05106 - CUJAS 22 RUE CUJAS - 75005 PARIS 48.8481913486, 2.34183165485 True False OPEN Paris 19 16 3 2017-09-14T00:08:26+00:00 5.0
2 14114 14114 - PLACE DE CATALOGNE 4 RUE ALAIN - 75014 PARIS 48.8372933094, 2.31748293064 True True OPEN Paris 43 0 0 2017-09-22T09:17:22+00:00 14.0
3 35007 35007 - DELESSERT (PANTIN) 1 RUE BENJAMIN DELESSERT - 93500 PANTIN 48.8939437132, 2.41802965969 True False OPEN Paris 21 4 16 2017-09-22T11:05:00+00:00 NaN
4 16001 16001 - AVENUE DES PORTUGAIS 2 AVENUE DES PORTUGAIS - 75016 PARIS 48.8712137122, 2.29369213365 True True OPEN Paris 26 10 16 2017-09-22T12:24:25+00:00 16.0
5 9034 09034 - GODOT DE MAUROY 2 RUE GODOT DE MAUROY - 75009 PARIS 48.8699566816, 2.32659965711 True False OPEN Paris 22 1 21 2017-09-22T12:56:20+00:00 9.0

Number of bike stands


In [135]:
bike_stands_arron = velibs.groupby('arron')['bike_stands'].sum()
plt.bar(bike_stands_arron.index.astype(int), bike_stands_arron.values)
plt.show()



In [136]:
# https://github.com/codeforamerica/click_that_hood/raw/master/public/data/paris.geojson
state_geo = r'paris.json'

m = folium.Map(location=[48.856614, 2.3522219], zoom_start=13, tiles='Stamen Toner')
m.choropleth(geo_path=state_geo, 
             data=bike_stands_arron,
             columns=['arron', 'bike_stands'],
             key_on='properties.cartodb_id',
             fill_color='PuBu', 
             fill_opacity=0.9, line_opacity=0.2,
             legend_name='Number of bike stands',
             highlight=1)
m


Out[136]:

Available bikes


In [137]:
from colour import Color
red = Color("red")
colors = list(red.range_to(Color("green").hex,10))
def red(brightness):
    brightness = int(round(9 * brightness)) # convert from 0.0-1.0 to 0-255
    return colors[brightness]

In [138]:
red(0).hex


Out[138]:
'#f00'

In [139]:
velibs['lat'] = velibs['position'].apply(lambda x: float(x.split(",")[0]))
velibs['long'] = velibs['position'].apply(lambda x: float(x.split(",")[1]))
velibs['color'] = velibs.apply(lambda x: red(x['available_bikes']/float(x['bike_stands'])).hex, axis=1)

In [140]:
m = folium.Map(location=[48.856614, 2.3522219], zoom_start=13, tiles='Stamen Toner')

m.add_child(plugins.HeatMap(velibs[['lat','long', 'available_bikes']].values, radius = 20), name='Training location')
colormap = cm.LinearColormap(['blue', 'green',  'yellow', 'orange', 'red'],  index=[0, 0.25, 0.5, 0.75, 1.0]).scale(0,200)
colormap.caption = 'Number of velibs'
m.add_child(colormap)
m


Out[140]:

In [141]:
m = folium.Map(location=[48.856614, 2.3522219], zoom_start=13, tiles='Stamen Toner')

for k,v in velibs.iterrows():
    folium.CircleMarker(location=[v.position.split(",")[0], v.position.split(",")[1]], 
                        fill_color=red(v.available_bikes/float(v.bike_stands)).hex,
                        popup= str(v.available_bikes) + " / " + str(v.bike_stands),
                        radius=7).add_to(m)

In [19]:
m


Out[19]:

Utilisation


In [211]:
df = pd.DataFrame(columns=np.append("Date",velibs.number.values))
path_data = 'data/raw/velib/'
for i, name in tqdm(enumerate(os.listdir(path_data))):
    #print(name.split("velib")[1].split(".")[0])
    velibs_temp = pd.read_csv(path_data + name, sep=";")
    positions = {}
    #print(name.split("velib")[2])
    positions['Date'] = name.split("velib")[1].split(".")[0]
    for k,v in velibs_temp.iterrows():
        positions[str(v.number)] = v.available_bikes
    
    df.loc[i] = pd.Series(positions)


0it [00:00, ?it/s]
1it [00:00,  3.68it/s]
2it [00:00,  3.70it/s]
3it [00:00,  3.73it/s]
4it [00:01,  3.73it/s]
5it [00:01,  3.79it/s]
6it [00:01,  3.73it/s]
7it [00:01,  3.69it/s]
8it [00:02,  3.70it/s]
9it [00:02,  3.76it/s]
10it [00:02,  3.80it/s]
11it [00:02,  3.62it/s]
3045it [14:37,  2.84it/s]

In [212]:
df.Date = pd.to_datetime(df.Date, format='%Y-%m-%d-%H-%M', errors='coerce')

In [213]:
df = df.sort_values('Date').reset_index(drop=True)

In [214]:
print(df.shape)
df.head()


(3045, 1187)
Out[214]:
Date 5106 14114 35007 16001 9034 8113 6006 42015 19028 ... 23005 19117 12043 10110 16122 7020 15123 1008 7006 1020
0 2017-05-07 09:32:00 0 1 15 2 11 6 4 1 0 ... 24 18 27 3 42 54 40 1 17 9
1 2017-05-07 10:32:00 0 1 14 0 11 8 5 1 1 ... 25 20 27 3 43 54 42 1 16 10
2 2017-05-07 11:32:00 0 2 15 1 7 8 5 1 1 ... 23 17 28 3 45 51 39 4 15 5
3 2017-05-07 12:33:00 2 3 16 0 7 10 6 1 0 ... 21 15 30 4 42 57 37 5 10 7
4 2017-05-07 13:33:00 0 0 12 3 9 12 2 3 2 ... 21 22 31 2 42 49 42 1 16 4

5 rows × 1187 columns


In [215]:
df.loc[:, df.columns != 'Date'].sum(axis=1).mean()


Out[215]:
13533.330157687253

In [284]:
df_grouped = df.groupby([df.Date.dt.dayofweek, df.Date.dt.hour]).agg({lambda x: np.mean(x)}).diff().clip(0).sum(axis=1)

In [303]:
plt.figure(figsize=(15,4))
for i in range(len(weekday)):
    plt.plot(df_grouped.loc[i], label=weekday[i])

plt.legend()
plt.show()


Save to json


In [289]:
weekday = ['monday','tuesday','wednesday','thursday','friday','saturday','sunday']
json_file = dict()
for i in range(len(weekday)):
    json_file[weekday[i]] = {'hour': df_grouped.loc[i].index.values.tolist(), 'velibs': df_grouped.loc[i].round(1).values.tolist()}

with open('output/velibs.json', 'w') as outfile:
    json.dump({'hourly': json_file}, 
              outfile)

In [34]:
import pygal
from IPython.display import SVG, HTML

In [35]:
html_pygal = """
<!DOCTYPE html>
<html>
  <head>
  <script type="text/javascript" src="http://kozea.github.com/pygal.js/javascripts/svg.jquery.js"></script>
  <script type="text/javascript" src="http://kozea.github.com/pygal.js/javascripts/pygal-tooltips.js"></script>
    <!-- ... -->
  </head>
  <body>
    <figure>
      {pygal_render}
    </figure>
  </body>
</html>
"""

In [38]:
line_chart = pygal.Line(x_title='Time [hour]',y_title='Number of used bikes')
line_chart.title = 'Number of used bikes'
line_chart.x_labels = range(0, 24)
weekday = ['monday','tuesday','wednesday','thursday','friday','saturday','sunday']
for i in range(15,22):
    subdf = df[df['Date'].dt.day == i]
    tmp_df = subdf.ix[:, df.columns != 'Date'].diff().clip(0).sum(axis=1).values[1:]
    line_chart.add(weekday[i-15], tmp_df)

HTML(html_pygal.format(pygal_render=line_chart.render()))


Out[38]:
b'\nNumber of used bikes1000100020002000300030004000400001234567891011121314151617181920212223Number of used bikesTime [hour]Number of used bikes73611.584615384615384430.116367077053636.76856187290969452.312713776130261.952508361204478.282439415237987.1364548494983469.7368459363325112.32040133779263475.7298595444387137.50434782608693468.8489920685893162.68829431438127412.69223491761852187.87224080267558306.26075249373179213.0561872909699158.9879921481497238.24013377926423345.6592678849955263.4240802675585405.811367441101133288.60802675585285386.056618878111270313.79197324414713370.852121389121200338.9759197324414378.620842733131041364.1598662207358396.26693836141062389.3438127090301393.936321956151228414.5277591973244375.513354195161851439.7117056856187306.371734226172735464.89565217391305208.263881814182886490.0795986622074191.505640055191897515.2635451505016301.266574485201337540.447491638796363.416345244211299565.6314381270903367.6336511172282811.584615384615384419.906047595058536.76856187290969446.874608835141661.952508361204465.630521796236487.1364548494983471.4015719383332112.32040133779263474.952987414378137.50434782608693469.84782766951098162.68829431438127389.9409795562424187.87224080267558242.77920093274527213.05618729096999.3846153846281972238.24013377926423292.94294447391171263.4240802675585381.839313005101179288.60802675585285380.951459137111487313.79197324414713346.769085219121382338.9759197324414358.422167237131009364.1598662207358399.818353832141124389.3438127090301387.055454479151178414.5277591973244381.06244087161652439.7117056856187328.457099192172477464.89565217391305236.897169056182770490.0795986622074204.379521141191842515.2635451505016307.370569828201503540.447491638796344.993377483211593565.6314381270903335.00502146922101411.584615384615384399.263445164074836.76856187290969428.784586275147861.952508361204458.749654319240787.1364548494983466.6293573983345112.32040133779263473.5102248744397137.50434782608693467.73917473351095162.68829431438127390.27392475162404187.87224080267558244.99883560174279213.056187290969936.908085292282012238.24013377926423288.50367513391089263.4240802675585390.939815152101275288.60802675585285370.297212721111557313.79197324414713339.000363875121391338.9759197324414357.423331635131067364.1598662207358393.381413289141036389.3438127090301396.821847027151237414.5277591973244374.514518594161683439.7117056856187325.016665454172735464.89565217391305208.263881814182819490.0795986622074198.9414162191004515.2635451505016400.37326249920863540.447491638796416.02168692221790565.6314381270903424.1233534682262011.584615384615384442.990248162048836.76856187290969457.639836984139061.952508361204468.516046867234687.1364548494983473.3992431413299112.32040133779263478.6153846154396137.50434782608693467.85015646651039162.68829431438127396.48890182762240187.87224080267558263.19983989573802213.056187290969989.846372170981791238.24013377926423313.03063823691036263.4240802675585396.821847027101294288.60802675585285368.188559785111293313.79197324414713368.299541518121190338.9759197324414379.730660068131061364.1598662207358394.04730369141156389.3438127090301383.504039007151219414.5277591973244376.512189797162596439.7117056856187223.69034277171589464.89565217391305335.44894840318693490.0795986622074434.88858161719946515.2635451505016406.81020304220679540.447491638796436.44232588621667565.6314381270903437.7741066882266911.584615384615384437.552143221061736.76856187290969443.323193363147261.952508361204459.41554472247087.1364548494983459.6375081873341112.32040133779263473.9541518084393137.50434782608693468.18310166751030162.68829431438127397.48773742861979187.87224080267558292.16607233873101213.0561872909699167.64456735381560238.24013377926423338.66741867491016263.4240802675585399.041481697101206288.60802675585285377.954952332111250313.79197324414713373.071756059121210338.9759197324414377.511025398131267364.1598662207358371.185066589141116389.3438127090301387.943308347151291414.5277591973244368.521504985162030439.7117056856187286.50600393172371464.89565217391305248.661232807182289490.0795986622074257.761734954191787515.2635451505016313.4745651720902540.447491638796411.69339931621794565.6314381270903423.6794265342286011.584615384615384416.354632123081236.76856187290969421.681755331167761.952508361204436.664289353256087.1364548494983449.6491521723487112.32040133779263457.7508187184424137.50434782608693464.7426679285463162.68829431438127460.4143803226817187.87224080267558421.12684666371065213.0561872909699393.60337675681268238.24013377926423371.07408485691250263.4240802675585373.071756059101365288.60802675585285360.308856706111436313.79197324414713352.429153628121178338.9759197324414381.06244087131262364.1598662207358371.739975257141163389.3438127090301382.727166873151283414.5277591973244369.409358853161533439.7117056856187341.663925478171585464.89565217391305335.892875337181585490.0795986622074335.892875337191338515.2635451505016363.30536351120999540.447491638796400.928171167211055565.6314381270903394.71319409122111211.584615384615384388.3872352810100736.76856187290969400.040317299187561.952508361204414.68990612263787.1364548494983441.1035586933528112.32040133779263453.2005676444349137.50434782608693473.066297945348162.68829431438127473.1772796746542187.87224080267558451.6468233757703213.0561872909699433.7787642828875238.24013377926423414.689906129999263.4240802675585400.928171167101082288.60802675585285391.716687286111420313.79197324414713354.204861364121170338.9759197324414381.950294738131123364.1598662207358387.166436213141063389.3438127090301393.825340223151295414.5277591973244368.077578051161740439.7117056856187318.690706644172074464.89565217391305281.622807656181895490.0795986622074301.488537952191473515.2635451505016348.322829488201162540.447491638796382.838148606211046565.6314381270903395.71202969222mondaytuesdaywednesdaythursdayfridaysaturdaysunday'