In questo notebook verrà effettuata l'analisi del dataset di craigslist, un noto portale americano che ospita annunci dedicati al lavoro, eventi, acquisti, incontri e altri servizi.
Il dataset è stato creato tramite web scraping dall'autore stesso.
### Keeping safe from any disaster :) ###
%autosave 30
Per queste analisi abbiamo utilizzato alcune librerie trattate durante il corso ed in parte altre basandoci sulla loro documentazione. Di seguito l'elenco:
plotly
seaborn
### Imports ###
import numpy as np
import pandas as pd
import plotly.express as px
import plotly.graph_objects as go
from plotly.subplots import make_subplots
import plotly
import seaborn as sns
import matplotlib.pyplot as plt
### Reading .csv dataset ###
df = pd.read_csv("resources/dataset/vehicles.csv")
df.head(1)
df.columns
df.info()
Tentando di lanciare la seguente cella notiamo subito come il tempo di computazione sia molto alto.
# plt.figure(figsize=(27,12))
# plt.title("Missing values for each column")
# dfT=df.isnull().transpose()
# fig=sns.heatmap(dfT)
# plt.savefig('density.png', dpi=300)
Il dataset considerato necessita tempi computazionali piuttosto onerosi per certe operazioni, quindi, abbiamo provato ad aggirare il problema considerando inizialmente solamente le osservazioni registrate tra il 2016 e il 2019.
Questo non è stato un approccio molto efficiente: da una parte i tempi di calcolo erano stati ridotti, ma dall'altro lato abbiamo perso dati importanti, riducendo il valore della raccolta.
### No more used ###
### Filtering more recent occurrences ###
# mask=(df["year"]>2015.0) & (df["year"]<2020)
### Applying the mask ###
# dfByYear=df[mask]
### Convert dataframe object to .csv file ###
# dfByYear.to_csv("resources/dataset/filteredByYear.csv", index=False)
### Reading from filesystem .csv dataset ###
#dfByYear = pd.read_csv("resources/dataset/filteredByYear.csv")
Abbiamo deciso quindi di considerare 80'000 campioni per i calcoli più lunghi.
### Dataframe sampling ###
dfs=df.sample(n=80000)
plt.figure(figsize=(27,12))
plt.title("Missing values for each column")
dfT=dfs.isnull().transpose()
fig=sns.heatmap(dfT)
plt.savefig('density.png', dpi=300)
### Dropping for semplicity useless columns ###
df = df.drop(columns=["url","region_url","image_url","county"])
La prima domanda ovvia che ci siamo posti è qual è il marchio più venduto su Craiglist.
Ci aspettavamo come risposta nè un marchio di lusso nè tanto meno uno di bassa qualità , altrimenti nessuno lo comprerebbe.
### Create a new grouped by manufacturer dataframe object###
occurrenciesByManufacturer = dfs.groupby(["manufacturer"]).size().reset_index(name="counts")
### Getting relative frequencies ###
occurrenciesByManufacturer["relative_freq"] = occurrenciesByManufacturer["counts"] /\
occurrenciesByManufacturer["counts"].sum()
### Sorting dataframe by number of occurrences for each manufacturer ###
occurrenciesByManufacturer = occurrenciesByManufacturer.sort_values(
"counts", ascending=True)
### Giving our char a name ###
title = "Number of traded cars for each manufacturer"
### Creating the bar chart ###
fig = px.bar(occurrenciesByManufacturer, y="manufacturer",
x="counts", text="counts", title=title, height=1000)
### Formatting values labels ###
fig.update_traces(texttemplate="%{text:.2s}", textposition="outside")
### Show ###
fig.show()
# save as html
#plotly.offline.plot(fig, filename=f"{figTitle}.html")
Le cause sono principalmente le seguenti:
La risposta, a nostro parere non così prevedibile, è il pickup.
Infatti, questa tipologia di veicolo viene molto acquistata e venduta nel territorio americano. Anche nei film viene vista come la tipica macchina usata dal buon padre di famiglia.
Alcune tra le motivazioni più rilevanti:
### Considering only for vehicles ###
result=df.loc[df["manufacturer"]=="ford", 'model']
### Create a new dataframe instance based on the previous mask ###
resultDf=pd.DataFrame(result)
### Creating the histogram ###
px.histogram(resultDf, barmode="overlay", y="model", height=1000)
### Taking some model labels and values
modelLabels = df[df["manufacturer"]=="ford"].model.value_counts().head(10).index
modelValues = df[df["manufacturer"]=="ford"].model.value_counts().head(10).values
### Making place for other traces ###
fig = make_subplots(rows=1, cols=1, specs=[[{"type":"domain"}]])
### Adding the new trace###
fig.add_trace(go.Pie(labels=modelLabels, values=modelValues, title="Ford models"),
1, 1)
### Show ###
fig.show()
### Optionally save it as .html/.png file ###
#plotly.offline.plot(fig, filename=f"{title}.html")
#fig.write_image(f"{title}.png")
Inizialmente, pensavamo che pickup fossero presenti maggiormente nelle zone aride/fuoristrada e meno nelle aree urbane.
Tuttavia, dal seguente grafico si evince, al contrario, come la distribuzione dei pickup sia densa specialmente nelle grandi città .
### Cleaning from non-specified values ###
df=df.dropna(subset=["manufacturer"])
### Creating first trace ###
fig=px.scatter_mapbox(df[df["type"]=="pickup"],
lat="lat", lon="long", hover_name="manufacturer",
hover_data=["odometer", "price", "state"], color_discrete_sequence=["blue"],
zoom=4, height=600)
### Customizing map ###
fig.update_layout(mapbox_style="open-street-map")
fig.update_layout(margin={"r":0,"t":0,"l":0,"b":0})
### Show ###
fig.show()
### Optionally save it as .html/.png file ###
#title="Geographical position of pickups"
#plotly.offline.plot(fig, filename=f"{title}.html")
#fig.write_image(f"{title}.png")
Pur confrontando la posizione di tutti i pickup con quella delle berline, quindi veicoli che tendenzialmente vengono utilizzati in aree urbane, non abbiamo riscontrato che esista un possibile schema logico che andasse incontro alle nostre aspettative.
### Cleaning from non-specified values ###
df=df.dropna(subset=["manufacturer"])
### Creating first trace ###
fig=px.scatter_mapbox(df[df["type"]=="hatchback"],
lat="lat", lon="long", hover_name="manufacturer",
hover_data=["odometer", "price", "state"], color_discrete_sequence=["red"],
zoom=4, height=600)
### Customizing map ###
fig.update_layout(mapbox_style="open-street-map")
fig.update_layout(margin={"r":0,"t":0,"l":0,"b":0})
### Show ###
fig.show()
### Optionally save it as .html/.png file ###
#title="Geographical position of hatchbacks"
#plotly.offline.plot(fig, filename=f"{title}.html")
#fig.write_image(f"{title}.png")
Fino ai 3 anni di età si ha una crescita ben evidente.
Notiamo un picco massimo dopo 3 anni, e a partire dal quinto anno in poi il numero di macchine vendute tende a diminuire.
Arrivando a 12 anni si ha un punto di massimo locale, dovuto probabilmente alla crisi economica del 2008.
La gente necessitava di liquidità , quindi potrebbe aver iniziato a vendere più macchine rispetto a prima.
### Cleaning dataframe from null-year fields ###
df = df[df.year.notnull()]
### Computing ages for each vehicles ###
### For simplicity we will consider 2020 ###
df["age"] = df.year.apply(lambda x: int(2020-x))
### Considering only vehicles with age [0,30] ###
df = df[(df.age >= 0) & (df.age <= 30)]
### Creating a new instance grouping the datagrame by age ###
groupedByAge = df.groupby(["age"]).size().reset_index(name="counts").set_index("age")
### Chart title ###
title="Number of vehicles for each age"
### Creating the line chart ###
fig = px.line(groupedByAge, y="counts", title=title)
### Updating yaxes label ###
fig.update_yaxes(title_text="number of vehicles")
### Show ###
fig.show()
### Optionally save it as .html/.png file ###
#plotly.offline.plot(fig, filename=f"{title}.html")
#fig.write_image(f"{title}.png")
Si può evincere una schiacciante vittoria dei veicoli a benzina.
Dai dati raccolti da Craigslist non si nota una gran crescita dell’elettrico come dalle nostre aspettative.
Probabilmente non ne vengono ancora venduti abbastanza su Craigslist per poter fare un confronto con le alternative.
### Cleaning from non-specified values ###
df=df.dropna(subset=["year"])
df=df.dropna(subset=["fuel"])
### Chart title ###
title="Number of cars by fuel"
### Creating the histogram ###
fig=px.histogram(df, x="year", facet_col="fuel", width=800, title=title, facet_col_wrap=2)
### Customizing order shown ###
# fig.update_yaxes(title_text="number of vehicles")
### Show ###
fig.show()
### Optionally save it as .html/.png file ###
#plotly.offline.plot(fig, filename=f"{title}.html")
#fig.write_image(f"{title}.png")
Dal grafico, si può intuire come con l'avanzare del tempo, il prezzo, in generale, tende a salire, ciò dovuto sicuramente all'evoluzione tecnologica.
Per quanto riguarda il diesel, la mediana dei prezzi supera i 20k dal 2011, continuando a crescere costantemente con l'avanzare degli anni.
L'elettrico invece viene introdotto per la prima volta nell'usato dal 2014 su craiglist. Negli ultmi anni come si può evincere questo sta facendo irruzione nel mercato automobilistico. Quindi sul prezzo non è ancora possibile estrapolare delle analisi significative e concrete.
La mediana del benzina, in genere, tende ad aumentare leggermente senza brusche fluttuazioni di prezzo.
title="Boxplot price by fuel"
df["price"]=df["price"].dropna()
df=df.sort_values(by=["year"])
fig=px.box(df,y="fuel",orientation="h", x="price", animation_frame="year", range_x=[0, 100000])
fig.update_layout(title=title)
E' intuitivo vedere come, la media dei km percorsi aumenta in maniera proporzionale all'aumentare dell'età .
title="Average of odometer by car manufacturer"
fig=px.histogram(df,orientation="h",animation_frame="year", y="manufacturer",
x="odometer", histfunc="avg", height=1000)
fig.update_yaxes(categoryorder="total ascending")
fig.update_layout(title=title)
I SUV sono la tipologia di veicolo più frequente nel sito di craiglist. In generale si può evincere che il nero tende ed essere più frequente nella maggior parte dei veicoli. Si può subito osservare che i colori più frequenti per i SUV e le sedan sono:
x = df.type
y = df.paint_color
fig = go.Figure(go.Histogram2d(
x=x,
y=y
))
title = "Colors of vehicles by type"
xaxis_title= "Type of vehicle"
yaxis_title= "Color"
fig.update_layout(
title=title,
xaxis_title=xaxis_title,
yaxis_title=yaxis_title)
fig.show()
In generale possiamo dire che per la condizione ci si basa sull'onesta degli inserzionisti. Tralasciata questa possiamo dire sicuramente affermare che buona parte dei veicoli elettrici viene descritta "come nuovo", questo perché l'elettrico è stato introdotto recentemente nel mercato automobilistico.
df=df.dropna(subset=["condition"])
gasLabels = df[df["fuel"]=="gas"].condition.value_counts().head(10).index
gasValues = df[df["fuel"]=="gas"].condition.value_counts().head(10).values
dieselLabels = df[df["fuel"]=="diesel"].condition.value_counts().head(10).index
dieselValues = df[df["fuel"]=="diesel"].condition.value_counts().head(10).values
electricLabels = df[df["fuel"]=="electric"].condition.value_counts().head(10).index
electricValues = df[df["fuel"]=="electric"].condition.value_counts().head(10).values
hybridLabels = df[df["fuel"]=="hybrid"].condition.value_counts().head(10).index
hybridValues = df[df["fuel"]=="hybrid"].condition.value_counts().head(10).values
# Create subplots: use 'domain' type for Pie subplot
fig = make_subplots(rows=2, cols=2, specs=[[{'type':'domain'}, {'type':'domain'}], [{'type':'domain'}, {'type':'domain'}]])
fig.add_trace(go.Pie(labels=gasLabels, values=gasValues, name="Gas Car", title="Gas Cars"), 1, 1)
fig.add_trace(go.Pie(labels=dieselLabels, values=dieselValues, name="Diesel Car",title="Diesel Cars"), 1, 2)
fig.add_trace(go.Pie(labels=electricLabels, values=electricValues, name="Electric Car",title="Electric Cars"), 2, 1)
fig.add_trace(go.Pie(labels=hybridLabels, values=hybridValues, name="Hybrid Car",title="Hybrid Cars"), 2, 2)
# Use hole to create a donut-like pie chart
fig.update_traces(hole=.4, hoverinfo="label+percent+name")
fig.show()
Tendianzalmente la curva della media del prezzo tende a scendere in modo lineare con l'aumentare degli anni. Ciò è dovuto principalmente all'usura del veicolo e all'obsolescenza e senescenza che subisce nel tempo.
def display_price(df, age = (0,12), price = (100,100000), vehicle_type = "all", state = "all"):
# Display the median price of vehicles depending on its type and its state.
if state != "all":
df = df[df["state"] == state]
if vehicle_type != "all":
df = df[df["type"] == vehicle_type]
df = df[(df["age"] <= age[1]) & (df["age"] >= age[0])]
df = df[(df["price"] >= price[0]) & (df["price"] <= price[1])]
price_age = pd.pivot_table(df, values = "price", index = "age", aggfunc= np.median)
price_age.columns = ["Median Price"]
fig = plt.figure(figsize=(12,6))
ax = fig.add_axes([0,0,1,1])
ax2 = fig.add_axes([0.6,0.47,.35,.35])
ax.plot(price_age["Median Price"], lw = 5)
ax2.set_title(f"Vehicle type: {vehicle_type}\nNumber of vehicles: {df.shape[0]}\nCountry: USA\nUS-State: {state}", fontsize = 15)
ax2.axis('off')
ax.set_title(f"Median price by age of the vehicles",fontsize=25)
ax.set_ylim(0,price_age["Median Price"].max()+1000)
ax.set_xlabel("Age", fontsize = 15)
ax.set_ylabel("Median price in $", fontsize = 15)
ax.tick_params(axis='both', which='major', labelsize=15)
plt.show()
df = df.dropna(subset=["type"])
df = df[df.year.notnull()]
df["age"] = df.year.apply(lambda x: int(2020-x))
df = df[(df.age >= 0) & (df.age <= 30)]
for t in df.type.unique()[:5]:
display_price(df, vehicle_type=t)