Data's true power is its ability to definitively answer questions. In this project, I explored Python requests, APIs, and JSON traversals to answer a fundamental question: "What is the weather like as we approach the equator?" The answer may seem pretty obvious but this project seeks to prove that.
![](https://private-user-images.githubusercontent.com/107348074/237838344-5462064f-17bc-4b63-97f0-034eb771cc00.png?jwt=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJnaXRodWIuY29tIiwiYXVkIjoicmF3LmdpdGh1YnVzZXJjb250ZW50LmNvbSIsImtleSI6ImtleTUiLCJleHAiOjE3MTUyOTc1NzYsIm5iZiI6MTcxNTI5NzI3NiwicGF0aCI6Ii8xMDczNDgwNzQvMjM3ODM4MzQ0LTU0NjIwNjRmLTE3YmMtNGI2My05N2YwLTAzNGViNzcxY2MwMC5wbmc_WC1BbXotQWxnb3JpdGhtPUFXUzQtSE1BQy1TSEEyNTYmWC1BbXotQ3JlZGVudGlhbD1BS0lBVkNPRFlMU0E1M1BRSzRaQSUyRjIwMjQwNTA5JTJGdXMtZWFzdC0xJTJGczMlMkZhd3M0X3JlcXVlc3QmWC1BbXotRGF0ZT0yMDI0MDUwOVQyMzI3NTZaJlgtQW16LUV4cGlyZXM9MzAwJlgtQW16LVNpZ25hdHVyZT1hZmI5Y2NmYjY3YWQ0NWM2YjgxNmQxOTM3ZjVhNTkxOTllYWJjNzAxYWVhNTM3ZmFkNzEwYmQ2YmE0YWE0YzU4JlgtQW16LVNpZ25lZEhlYWRlcnM9aG9zdCZhY3Rvcl9pZD0wJmtleV9pZD0wJnJlcG9faWQ9MCJ9.FxKig2SimLkbptxF-4z154B6LIyjkhCbv7pE8ufz1jE)
I created a set of latitudes and longitudes using the random function from numpy (Example 1) and the used these cordinates combinations to identify the nearest cities. Citipy library was used to access the nearest cities based on the coordinates (Example 2). Note that the city data generated is based on random coordinates and would thus differ for each query.
#Example 1: create a set of random lat and lng combinations
lats = np.random.uniform(lat_range[0], lat_range[1], size=1500)
lngs = np.random.uniform(lng_range[0], lng_range[1], size=1500)
lat_lngs = zip(lats, lngs)
#Example 2: Identify nearest city for each lat, lng combination
for lat_lng in lat_lngs:
city = citipy.nearest_city(lat_lng[0], lat_lng[1]).city_name
if city not in cities:
cities.append(city)
The first step was to make a call, using the OpenWeatherMap API (Example 3), for latitude and longitude coordinates as well as weather information (e.g., max temp, humidity, cloudiness, wind speed, country, and date for each of the cities generated above. The try/except method was used to by-pass missing data (Example 4).
#Example 3: Set the API base URL
# Save config information.
url = "http://api.openweathermap.org/data/2.5/weather?"
units = "metric"
# Build partial query URL
query_url= f"{url}appid={weather_api_key}&units={units}&q=
# Loop through all the cities in our list to fetch weather data
for i, city in enumerate(cities):
# Create endpoint URL with each city
city_url = query_url + city
#Example 4: Try/except method was used to by-pass missing data for the coordinates and weather informations
# Run an API request for each of the cities
try:
# Parse the JSON and retrieve data
city_weather = requests.get(city_url).json()
# Parse out latitude, longitude, max temp, humidity, cloudiness, wind speed, country, and date
city_lat = city_weather["coord"]["lat"]
city_lng = city_weather["coord"]["lon"]
city_max_temp = city_weather["main"]["temp_max"]
city_humidity = city_weather["main"]["humidity"]
city_clouds = city_weather["clouds"]["all"]
city_wind = city_weather["wind"]["speed"]
city_country = city_weather["sys"]["country"]
city_date = city_weather["dt"]
# If an error is experienced, skip the city
except:
pass
After using the OpenWeatherMap API to retrieve weather data from the cities list generated in step above, I created a series of scatter plots to showcase the following relationships: Latitude vs. Temperature (Plot 1), Latitude vs. Humidity, Latitude vs. Cloudiness, Latitude vs. Wind Speed.
Plot 1: Chart showing relationship between Latitude and Temperature
![](https://private-user-images.githubusercontent.com/107348074/237871789-64ee1cd3-7666-44ff-9cde-74db44f9f27b.png?jwt=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJnaXRodWIuY29tIiwiYXVkIjoicmF3LmdpdGh1YnVzZXJjb250ZW50LmNvbSIsImtleSI6ImtleTUiLCJleHAiOjE3MTUyOTc1NzYsIm5iZiI6MTcxNTI5NzI3NiwicGF0aCI6Ii8xMDczNDgwNzQvMjM3ODcxNzg5LTY0ZWUxY2QzLTc2NjYtNDRmZi05Y2RlLTc0ZGI0NGY5ZjI3Yi5wbmc_WC1BbXotQWxnb3JpdGhtPUFXUzQtSE1BQy1TSEEyNTYmWC1BbXotQ3JlZGVudGlhbD1BS0lBVkNPRFlMU0E1M1BRSzRaQSUyRjIwMjQwNTA5JTJGdXMtZWFzdC0xJTJGczMlMkZhd3M0X3JlcXVlc3QmWC1BbXotRGF0ZT0yMDI0MDUwOVQyMzI3NTZaJlgtQW16LUV4cGlyZXM9MzAwJlgtQW16LVNpZ25hdHVyZT1hNWY5ZGE2ODc0ODU0ODQ1ZTg3MWI5ODZkMzRkNjkzYzkzZGM5MTVkY2U5MzRhYTBhNWVkY2Y1NGRkZmI1OWUwJlgtQW16LVNpZ25lZEhlYWRlcnM9aG9zdCZhY3Rvcl9pZD0wJmtleV9pZD0wJnJlcG9faWQ9MCJ9.yl2J_Omd4X6mvrkEkWFYxUoYG77GVvC2pJ2tdjPuIIU)
In this step, I computed the linear regression for each relationship created above by separating the plots into Northern Hemisphere (greater than or equal to 0 degrees latitude) and Southern Hemisphere (less than 0 degrees latitude). To save time, I defined a function (Example 5) in order to create the linear regression plots.
#Example 5: Function to create Linear Regression plots
def plot_linear_regresssion (x_values, y_values, title):
#regression line
(slope, intercept, rvalue, pvalue, stderr) = linregress(x_values, y_values)
y_regress = slope * x_values + intercept
line_eqn = f"y={round(slope,2)}x+{round(intercept,2)}"
#plot
plt.figure(figsize = (9,7))
plt.scatter(x_values, y_values, s = 60)
plt.plot(x_values, y_regress, "r-")
#text coordinates
text_y_coord = max(y_values)/10
if min(x_values) < 0:
text_x_coord = min(x_values)
elif max(x_values) > 0:
text_x_coord = max(x_values)/10
#line equation
plt.annotate(line_eqn,(text_x_coord,text_y_coord), fontsize=12,color="red", size= 16)
print(f"The r-squared value is {rvalue**2}")
#label and graph properties
plt.ylabel(title.split("vs")[0], size = 14 )
plt.xlabel(title.split("vs")[1], size = 14)
plt.yticks(size = 14)
plt.xticks(size = 14)
plt.show()
Plot 2: An example of Scatter Plot showing linear regression between Max Temperature and Latitude in the Northern Hemisphere (i.e., latitude >= 0)
![](https://private-user-images.githubusercontent.com/107348074/237867319-f270d29a-7492-431f-8a53-1bc79731556a.png?jwt=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJnaXRodWIuY29tIiwiYXVkIjoicmF3LmdpdGh1YnVzZXJjb250ZW50LmNvbSIsImtleSI6ImtleTUiLCJleHAiOjE3MTUyOTc1NzYsIm5iZiI6MTcxNTI5NzI3NiwicGF0aCI6Ii8xMDczNDgwNzQvMjM3ODY3MzE5LWYyNzBkMjlhLTc0OTItNDMxZi04YTUzLTFiYzc5NzMxNTU2YS5wbmc_WC1BbXotQWxnb3JpdGhtPUFXUzQtSE1BQy1TSEEyNTYmWC1BbXotQ3JlZGVudGlhbD1BS0lBVkNPRFlMU0E1M1BRSzRaQSUyRjIwMjQwNTA5JTJGdXMtZWFzdC0xJTJGczMlMkZhd3M0X3JlcXVlc3QmWC1BbXotRGF0ZT0yMDI0MDUwOVQyMzI3NTZaJlgtQW16LUV4cGlyZXM9MzAwJlgtQW16LVNpZ25hdHVyZT04NjlkOTNkMWFiNDZiMDZhMzM1ZmY3ZjYwZDUyMmM0MzQwODUxMTU4ZDMzNGIwMWFjYTM1ZTU0ODMxYmM2OWJlJlgtQW16LVNpZ25lZEhlYWRlcnM9aG9zdCZhY3Rvcl9pZD0wJmtleV9pZD0wJnJlcG9faWQ9MCJ9.Xs-s786EM9sd8Rp3Z7s9tlus_GrLgHGesvjIh4SY70c)
Plot 3: Scatter Plot showing linear regression between Max Temperature and Latitude in the Southern Hemisphere (i.e., latitude < 0)
![](https://private-user-images.githubusercontent.com/107348074/237868031-4f4f24e2-0bc9-4412-ba21-e5b97ffd9ef4.png?jwt=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJnaXRodWIuY29tIiwiYXVkIjoicmF3LmdpdGh1YnVzZXJjb250ZW50LmNvbSIsImtleSI6ImtleTUiLCJleHAiOjE3MTUyOTc1NzYsIm5iZiI6MTcxNTI5NzI3NiwicGF0aCI6Ii8xMDczNDgwNzQvMjM3ODY4MDMxLTRmNGYyNGUyLTBiYzktNDQxMi1iYTIxLWU1Yjk3ZmZkOWVmNC5wbmc_WC1BbXotQWxnb3JpdGhtPUFXUzQtSE1BQy1TSEEyNTYmWC1BbXotQ3JlZGVudGlhbD1BS0lBVkNPRFlMU0E1M1BRSzRaQSUyRjIwMjQwNTA5JTJGdXMtZWFzdC0xJTJGczMlMkZhd3M0X3JlcXVlc3QmWC1BbXotRGF0ZT0yMDI0MDUwOVQyMzI3NTZaJlgtQW16LUV4cGlyZXM9MzAwJlgtQW16LVNpZ25hdHVyZT1hMjg2OTczN2FjNTBjZDQyNjY0ZTQ3ZWMxNzQ2YjMwMmU4ZmQ4MWQ1Mzg0N2ZjMGViOGRjZGJhZjY0OWJjZDhkJlgtQW16LVNpZ25lZEhlYWRlcnM9aG9zdCZhY3Rvcl9pZD0wJmtleV9pZD0wJnJlcG9faWQ9MCJ9.cz1nQqQzq34kuM9jKrMj666mLITK2n1CRUTeAL65izw)
Lastly, I filtered for my ideal city using some specific criteria (Example 6), queried the first hotel located wihtin 10km of coordinates (Example 7) and rendered their locations on a map plot (Plot 4). The criteria used for selecting my ideal city included cities with max temperatures lower than 27 degrees but higher than 21, cites with wind speed less than 4.5 m/s and cities with clear skies (i.e., zero cloudiness). In order to ensure that no key or value error arise due to unavailable data, I used the try/except method to by pass missing data (Example 8).
#Example 6: Narrow down cities that fit criteria and drop any results with null values
df = df.loc[(df["Max Temp"] < 27) & (df["Max Temp"] > 21) & (df["Wind Speed"] < 4.5) & (df["Cloudiness"] == 0)]
#Drop any rows with null values using the dropna() function
df = df.dropna()
#Example 7: Set parameters to query hotel nearest to the city
radius =10000
params = {"categories": "accommodation.hotel", "apiKey": geoapify_key}
# Set base URL
base_url = "https://api.geoapify.com/v2/places"
# Make and API request using the params dictionary
response = requests.get(base_url, params)
# Convert the API response to JSON format
data = response.json()
#Example 8: Try/Except method to by pass missing data- Grab the first hotel from the results and store the name in the hotel_df DataFrame
try:
df.loc[index, "col"] = name_address["features"][0]["properties"]["name"]
except (KeyError, IndexError):
# If no hotel is found, set the hotel name as "No hotel found".
df.loc[index, "col"] = "No hotel found"
Plot 4: Map showing all Cities generated using CityPy
![](https://private-user-images.githubusercontent.com/107348074/237852166-14b461d6-caf2-444f-b1ba-9d0e7e108c06.png?jwt=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJnaXRodWIuY29tIiwiYXVkIjoicmF3LmdpdGh1YnVzZXJjb250ZW50LmNvbSIsImtleSI6ImtleTUiLCJleHAiOjE3MTUyOTc1NzYsIm5iZiI6MTcxNTI5NzI3NiwicGF0aCI6Ii8xMDczNDgwNzQvMjM3ODUyMTY2LTE0YjQ2MWQ2LWNhZjItNDQ0Zi1iMWJhLTlkMGU3ZTEwOGMwNi5wbmc_WC1BbXotQWxnb3JpdGhtPUFXUzQtSE1BQy1TSEEyNTYmWC1BbXotQ3JlZGVudGlhbD1BS0lBVkNPRFlMU0E1M1BRSzRaQSUyRjIwMjQwNTA5JTJGdXMtZWFzdC0xJTJGczMlMkZhd3M0X3JlcXVlc3QmWC1BbXotRGF0ZT0yMDI0MDUwOVQyMzI3NTZaJlgtQW16LUV4cGlyZXM9MzAwJlgtQW16LVNpZ25hdHVyZT1jZmI4MGNkMDEwNjViZmUxMDBhMjRiMWFjYzFmYTNkM2VkOWRhNTFkZDRiMWVhOGZmZjhjNWI3ZmIzYjhjMzFkJlgtQW16LVNpZ25lZEhlYWRlcnM9aG9zdCZhY3Rvcl9pZD0wJmtleV9pZD0wJnJlcG9faWQ9MCJ9.RtdpTMjW8qKn5jk5z3YKHSSg2DPa7vMztUdmJIW9Pz4)
Plot 5: My Ideal Cities (based on these criteria: > 21
![](https://private-user-images.githubusercontent.com/107348074/237852338-e8a9d71d-9427-42b8-99ac-96d19e088a19.png?jwt=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJnaXRodWIuY29tIiwiYXVkIjoicmF3LmdpdGh1YnVzZXJjb250ZW50LmNvbSIsImtleSI6ImtleTUiLCJleHAiOjE3MTUyOTc1NzYsIm5iZiI6MTcxNTI5NzI3NiwicGF0aCI6Ii8xMDczNDgwNzQvMjM3ODUyMzM4LWU4YTlkNzFkLTk0MjctNDJiOC05OWFjLTk2ZDE5ZTA4OGExOS5wbmc_WC1BbXotQWxnb3JpdGhtPUFXUzQtSE1BQy1TSEEyNTYmWC1BbXotQ3JlZGVudGlhbD1BS0lBVkNPRFlMU0E1M1BRSzRaQSUyRjIwMjQwNTA5JTJGdXMtZWFzdC0xJTJGczMlMkZhd3M0X3JlcXVlc3QmWC1BbXotRGF0ZT0yMDI0MDUwOVQyMzI3NTZaJlgtQW16LUV4cGlyZXM9MzAwJlgtQW16LVNpZ25hdHVyZT0zMWExNWYyZDJlMmEwYWMwY2EzYmNjMDY0OWYwZDZjOTRjYzM3ZmRkN2RkODQ4MDgzMmYzZjMxNjU3MTA0MjEzJlgtQW16LVNpZ25lZEhlYWRlcnM9aG9zdCZhY3Rvcl9pZD0wJmtleV9pZD0wJnJlcG9faWQ9MCJ9.NIYH7ap7QtNIW6xyo3lwsveBjS6DUslgpCU6oLRuWRM)
- From Plot 1, temperature becomes less warmer as we approach higher latitudes in the Northern hemisphere (latitudes >= 0) and vice-versa. On the other hand, temperatures feels warmer as we near the equator and vice-versa in the southern hemisphere (latitudes < 0).
- As observed in Plot 2, aprroximately 70% of the variance in the response varaible- Max temperature, can be explained by the independent variable- Latitude. Whereas, in plot 3, only about 60% of the variability in the outcome data can be explained by the model.
Data for this dataset was generated by edX Boot Camps LLC, and is intended for educational purposes only.