As electronic vehicles (EVs) become more popular, there is an increasing need for access to charging stations, also known as ports. To that end, many modern apartment buildings have begun retrofitting their parking garages to include shared charging stations. A charging station is shared if it is accessible by anyone in the building.
But with increasing demand comes competition for these ports — nothing is more frustrating than coming home to find no charging stations available! In this project, you will use a dataset to help apartment building managers better understand their tenants’ EV charging habits.
The data has been loaded into a PostgreSQL database with a table named charging_sessions
with the following columns:
charging_sessions
Column | Definition | Data type |
---|---|---|
garage_id | Identifier for the garage/building | VARCHAR |
user_id | Identifier for the individual user | VARCHAR |
user_type | Indicating whether the station is Shared or Private | VARCHAR |
start_plugin | The date and time the session started | DATETIME |
start_plugin_hour | The hour (in military time) that the session started | NUMERIC |
end_plugout | The date and time the session ended | DATETIME |
eng_plugin_hour | The hour (in military time) that the session ended | NUMERIC |
duration_hours | The length of the session, in hours | NUMERIC |
el_kwh | Amount of electricity used (in Kilowatt hours) | NUMERIC |
month_plugin | The month that the session started | VARCHAR |
weekdays_plugin | The day of the week that the session started | VARCHAR |
Let’s get started!
Sources
SELECT *
FROM public.charging_sessions
print(charging_sessions.info())
WITH clean_charging_sessions AS (
SELECT *
FROM public.charging_sessions
WHERE end_plugout IS NOT NULL
AND end_plugout_hour IS NOT NULL
AND duration_hours IS NOT NULL
)
SELECT * FROM clean_charging_sessions
-- unique_users_per_garage
-- Modify the code below
WITH clean_charging_sessions AS (
SELECT *
FROM public.charging_sessions
WHERE end_plugout IS NOT NULL
AND end_plugout_hour IS NOT NULL
AND duration_hours IS NOT NULL
)
SELECT
garage_id,
COUNT(DISTINCT user_id) as num_unique_users
FROM clean_charging_sessions
GROUP BY garage_id
ORDER BY num_unique_users DESC
-- most_popular_shared_start_times
WITH clean_charging_sessions AS (
SELECT *
FROM public.charging_sessions
WHERE end_plugout IS NOT NULL
AND end_plugout_hour IS NOT NULL
AND duration_hours IS NOT NULL
)
SELECT
start_plugin_hour,
COUNT(*) as num_users
FROM clean_charging_sessions
GROUP BY start_plugin_hour
ORDER BY start_plugin_hour DESC
import matplotlib.pyplot as plt
import pandas as pd
def ChartPopStartTimes(df):
column = df.columns
x = df[column[0]]
y = df[column[1]]
plt.bar(x, y, color='xkcd:bright blue')
plt.grid(axis='y')
plt.xlabel("Hour (Military Time) ")
plt.ylabel("Number of Users")
plt.title("Electric Charging Activity | Hour")
plt.show()
print(ChartPopStartTimes(most_popular_shared_start_times))
The visualization suggests an exponential increase in the number of users from 5 AM to 4 PM, reaching a peak, followed by a decline as nighttime sets in. This pattern indicates that user activity ramps up sharply in the morning, continues to grow steadily throughout the day, and then drops off during the evening and night. This type of trend is typical for activities or services that are heavily used during the day and less so during the night, reflecting natural human activity patterns.
import numpy as np
def DescStats(df):
duration = df['duration_hours']
stats = duration.describe()
return stats
print(DescStats(df))
-- long_duration_shared_users
WITH clean_charging_sessions AS (
SELECT *
FROM public.charging_sessions
WHERE end_plugout IS NOT NULL
AND end_plugout_hour IS NOT NULL
AND duration_hours IS NOT NULL
)
SELECT
user_type,
duration_hours
FROM clean_charging_sessions
WHERE
duration_hours > 10.03
AND user_type = 'Shared'
from IPython.core.display import display, HTML
tableau_viz_html = """
<div class='tableauPlaceholder' id='viz1719896411049' style='position: relative'>
<noscript>
<a href='#'>
<img alt='Dashboard 1 ' src='https://public.tableau.com/static/images/El/ElectricVehicleChargingActivity/Dashboard1/1_rss.png' style='border: none' />
</a>
</noscript>
<object class='tableauViz' style='display:none;'>
<param name='host_url' value='https%3A%2F%2Fpublic.tableau.com%2F' />
<param name='embed_code_version' value='3' />
<param name='site_root' value='' />
<param name='name' value='ElectricVehicleChargingActivity/Dashboard1' />
<param name='tabs' value='no' />
<param name='toolbar' value='yes' />
<param name='static_image' value='https://public.tableau.com/static/images/El/ElectricVehicleChargingActivity/Dashboard1/1.png' />
<param name='animate_transition' value='yes' />
<param name='display_static_image' value='yes' />
<param name='display_spinner' value='yes' />
<param name='display_overlay' value='yes' />
<param name='display_count' value='yes' />
<param name='language' value='en-US' />
<param name='filter' value='publish=yes' />
</object>
</div>
<script type='text/javascript'>
var divElement = document.getElementById('viz1719896411049');
var vizElement = divElement.getElementsByTagName('object')[0];
if (divElement.offsetWidth > 800) {
vizElement.style.width='1200px';
vizElement.style.height='627px';
} else if (divElement.offsetWidth > 500) {
vizElement.style.width='1200px';
vizElement.style.height='627px';
} else {
vizElement.style.width='100%';
vizElement.style.height='927px';
}
var scriptElement = document.createElement('script');
scriptElement.src = 'https://public.tableau.com/javascripts/api/viz_v1.js';
vizElement.parentNode.insertBefore(scriptElement, vizElement);
</script>
"""
display(HTML(tableau_viz_html))