Week 6 Data Visualization
Week 6: Mapplotlib, Seaborn and Plotly
Introduction to Matplotlib¶
Matplotlib
is the most widely used library for creating static, interactive, and animated visualizations in Python. It offers extensive functionality to create a wide variety of plots, from simple line plots
to complex visualizations like heatmaps
and 3D plots
.
Why Use Matplotlib
?¶
Simplicity and Flexibility:
Matplotlib
provides a simple interface for quickly generating basic plots, while also offering extensive control over the appearance and behavior ofplots
. You can customize every aspect of your figures, fromcolors
andmarkers
toaxes
andlabels
.Wide Range of Plot Types: Whether you need basic
line plots
,bar charts
,histograms
,scatter plots
, or more specialized visualizations likepie charts
, Matplotlib can handle it all. It also supports more advanced features such assubplots
andmultiple axes
.Integration with Other Libraries:
Matplotlib
integrates well with other libraries such asPandas
andNumPy
, making it easy to plot data directly from these popular data science tools. It’s commonly used alongsideSeaborn
, a higher-level statistical visualization library that builds onMatplotlib
for more aesthetically pleasing plots.Publication-Quality Figures: One of the most powerful features of Matplotlib is its ability to generate high-quality, publication-ready figures in various formats (e.g.,
PNG
,PDF
,SVG
).
Setting up Matplotlib¶
To begin with, let's ensure that we have matplotlib installed and set up:
!pip install matplotlib
!pip install matplotlib
Defaulting to user installation because normal site-packages is not writeable Requirement already satisfied: matplotlib in c:\programdata\anaconda3\lib\site-packages (3.8.0) Requirement already satisfied: contourpy>=1.0.1 in c:\programdata\anaconda3\lib\site-packages (from matplotlib) (1.2.0) Requirement already satisfied: cycler>=0.10 in c:\programdata\anaconda3\lib\site-packages (from matplotlib) (0.11.0) Requirement already satisfied: fonttools>=4.22.0 in c:\programdata\anaconda3\lib\site-packages (from matplotlib) (4.25.0) Requirement already satisfied: kiwisolver>=1.0.1 in c:\programdata\anaconda3\lib\site-packages (from matplotlib) (1.4.4) Requirement already satisfied: numpy<2,>=1.21 in c:\programdata\anaconda3\lib\site-packages (from matplotlib) (1.26.4) Requirement already satisfied: packaging>=20.0 in c:\programdata\anaconda3\lib\site-packages (from matplotlib) (23.1) Requirement already satisfied: pillow>=6.2.0 in c:\programdata\anaconda3\lib\site-packages (from matplotlib) (10.2.0) Requirement already satisfied: pyparsing>=2.3.1 in c:\programdata\anaconda3\lib\site-packages (from matplotlib) (3.0.9) Requirement already satisfied: python-dateutil>=2.7 in c:\programdata\anaconda3\lib\site-packages (from matplotlib) (2.8.2) Requirement already satisfied: six>=1.5 in c:\programdata\anaconda3\lib\site-packages (from python-dateutil>=2.7->matplotlib) (1.16.0)
import matplotlib.pyplot as plt
import pandas as pd
plt.rcParams['figure.figsize'] = [10, 5] # Set the size of the plot
Basic Plot and Workflow¶
Every plot in Matplotlib
follows a standard structure:
Figure: The entire image or plot, which can contain multiple subplots.
Axis: Represents the horizontal (x) and vertical (y) scales on the graph, including
ticks
andlabels
.Plot Elements: These include
lines
,markers
,bars
, etc., that represent data points on theaxes
.
Example: Basic Line Plot¶
# Sample data
x = [1, 2, 3, 4, 5]
y = [2, 4, 6, 8, 10]
# Creating a simple line plot
plt.plot(x, y)
# Adding title and labels
plt.title("Temperature")
plt.xlabel("Year")
plt.ylabel("Temp")
# Display the plot
plt.show()
Text(0, 0.5, 'Temp')
You can modify line styles
and colors
by adding parameters to the plt.plot()
function.
Simple linestyles
can be defined using the strings "solid
", "dotted
", "dashed
" or "dashdot
".Matplotlib
supports multiple categories ofmarkers
which are selected using themarker
parameter of plot commands:
plt.plot(x, y, linestyle='solid', color='g', marker='s')
plt.title("Styled Line Plot")
plt.show()
Additional parameters include:
xlabel/ylabel
: Defines labels for the axes.legend(loc)
: Displays a legend, with loc specifying the position (e.g., 'upper left').grid
: Adds gridlines to the plot.
# Create a basic line plot
plt.plot(x, y, marker='D', linestyle='dashed', color='r', label="Quadratic")
# Customize the plot
plt.title("Basic Line Plot") # Set title of the plot
plt.xlabel("X-axis") # Label for x-axis
plt.ylabel("Y-axis") # Label for y-axis
plt.legend(loc="upper right") # Add legend
plt.grid(True) # Display gridlines
plt.show()
Exmaple: Basic Scatter Plot¶
s
: Sets the size of themarkers
.c
: Sets the color of themarkers
.
# Sample data
x = [5, 7, 8, 7, 2, 17, 2, 9]
y = [99, 86, 87, 88, 100, 86, 103, 87]
size = [100,100,160,200,60,40,60,80]
colors = [1,2,3,4,5,6,7,8]
# Create a scatter plot
plt.scatter(x, y, c=colors, marker='o', s=size)
# Customize the plot
plt.title("Scatter Plot") # Set title
plt.xlabel("X-axis") # Label for x-axis
plt.ylabel("Y-axis") # Label for y-axis
plt.show()
Example: Basic Histogram¶
bins
: Specifies the number ofbins
for the histogram.edgecolor
: Defines thecolor
of thebin edges
.
# Sample data
data = [1, 2, 2, 2, 3, 3, 4, 4, 5, 5, 6, 7]
# Create a histogram
plt.hist(data, bins=7, color='g', edgecolor='black')
# Customize the plot
plt.title("Histogram Plot")
plt.xlabel("Value")
plt.ylabel("Frequency")
plt.show()
Example: Basic Bar Plot¶
# Sample data
categories = ['A', 'B', 'C', 'D', 'E']
values = [5, 7, 3, 8, 6]
# Define colors for each category
colors = ['red', 'green', 'blue', 'orange', 'purple']
# Create a bar plot with different colors for each category
plt.bar(categories, values, color=colors)
plt.title("Basic Bar Plot with Custom Colors")
plt.show()
Example: Horizontal bar chart¶
plt.barh(y, width, color)
:y
: Labels for the bars (y-axis).width
: The length of each bar (values).color
: Specifies the color for each bar.
sorted(iterable, key, reverse)
:iterable
: The data to be sorted.key
: Specifies the function used to extract a comparison key from each element (optional).reverse
: Whether to sort in descending order (default is ascending).
plt.text(x, y, s, va, fontweight)
:x
: The x-coordinate where the text will appear.y
: The y-coordinate where the text will appear.s
: The string to display (in this case, the value of the bar).va
: Vertical alignment of the text.fontweight
: Sets the weight of the text (e.g., 'bold').
categories = ['A', 'B', 'C', 'D', 'E']
values = [5, 7, 3, 8, 6]
# Define colors for each category
colors = ['red', 'green', 'blue', 'orange', 'purple']
# Sort the categories and values based on the values
sorted_data = sorted(zip(values, categories, colors))
print(sorted_data)
values_sorted, categories_sorted, colors_sorted = zip(*sorted_data)
print(values_sorted)
print(categories_sorted)
print(colors_sorted)
[(3, 'C', 'blue'), (5, 'A', 'red'), (6, 'E', 'purple'), (7, 'B', 'green'), (8, 'D', 'orange')] (3, 5, 6, 7, 8) ('C', 'A', 'E', 'B', 'D') ('blue', 'red', 'purple', 'green', 'orange')
demo = [3,2,5,6,4,8]
for i, value in enumerate(demo):
print(str(i) + ' ' + str(value))
0 3 1 2 2 5 3 6 4 4 5 8
# Sample data
categories = ['A', 'B', 'C', 'D', 'E']
values = [5, 7, 3, 8, 6]
# Define colors for each category
colors = ['red', 'green', 'blue', 'orange', 'purple']
# Sort the categories and values based on the values
sorted_data = sorted(zip(values, categories, colors))
values_sorted, categories_sorted, colors_sorted = zip(*sorted_data)
# Create a horizontal bar plot with sorted data
plt.barh(y = categories_sorted, width = values_sorted, color=colors_sorted)
plt.title("Horizontal Bar Plot (Sorted by Value)")
# Add annotation (text) on top of each bar
for i, value in enumerate(values_sorted):
plt.text(x = value+0.2, y = i, s = str(value), va='center', fontweight='bold')
plt.show()
Example: Basic Pie Chart¶
labels
: Defines the labels for each slice of the pie.autopct
: Displays the percentage value on each slice.startangle
: Sets the starting angle for the first slice.
# Sample data
labels = ['A', 'B', 'C', 'D']
sizes = [20, 30, 25, 25]
colors = ['gold', 'yellowgreen', 'lightcoral', 'lightskyblue']
# Create a pie chart
plt.pie(x = sizes, labels=labels, colors=colors, autopct='%1.4f%%', startangle=180)
# Customize the plot
plt.title("Pie Chart Example")
plt.axis('equal') # Ensures the pie is drawn as a circle
plt.show()
Example: Basic Box Plot¶
patch_artist
: IfTrue
, the boxes are drawn with a fill color.
# Sample data
data = [[1, 2, 5, 6, 8], [2, 3, 4, 7, 9], [3, 5, 6, 8, 10]]
# Create a box plot
plt.boxplot(data, patch_artist=True)
# Customize the plot
plt.title("Box Plot")
plt.xlabel("Dataset")
plt.ylabel("Values")
plt.show()
Example: Basic Subplots¶
Subplots
allow multiple plots in one figure for better comparison.
# Sample data
data = [1, 1, 2, 2, 2, 3, 4, 4, 5, 6, 6, 6, 7, 8, 9]
# Create subplots
fig, (ax1, ax2) = plt.subplots(2, 1)
# First subplot - Line plot
ax1.plot(data)
ax1.set_title('Line Plot')
# Second subplot - Bar plot
ax2.hist(data, bins=6)
ax2.set_title('Histogram')
# Show both plots
plt.tight_layout()
plt.show()
Saving Your Plots¶
You can save your visualizations in various formats, such as PNG
, JPG
, PDF
, and SVG
:
# Sample data
categories = ['A', 'B', 'C', 'D', 'E']
values = [5, 7, 3, 8, 6]
# Define colors for each category
colors = ['red', 'green', 'blue', 'orange', 'purple']
# Create a basic bar plot
plt.bar(categories, values,color=colors)
plt.title("Basic Bar Plot")
plt.savefig('./output/plot.png')
Additional Learning Resources:¶
Matplotlib Documentation: Official Documentation
Matplotlib Tutorials: Matplotlib Tutorials on Real Python
Hands-On¶
In this tutorial, we'll use Worcester Police Use of Force Incidents (July 2024) to solve various problems using techiques in matplotlib
.
Key Questions and Visualizations:
What is the
time trend
of incidents over the month of July?- Visualization: Line plot to observe the trend over time.
What is the
percentage distribution
of incidents involving injuries?- Visualization: Pie chart to analyze the proportion of incidents resulting in injury vs. no injury.
Which techniques were
most frequently
used in incidents?- Visualization: Bar plot to visualize the frequency of different use-of-force techniques.
What are the top locations for incidents?
- Visualization: Horizontal bar plot to analyze location frequency.
Load data¶
data = pd.read_csv('F:/Clark_Universiy/Clark_Teaching/Git_Repo/ssj-302/docs/Lectures/data/Worcester_Police_Use_of_Force_Incidents_(July_2024).csv')
data.head(3)
Incident_No | Date___Time | Location | Officer | Narr | Implement | Injury | Supervisor | Notified | ObjectId | |
---|---|---|---|---|---|---|---|---|---|---|
0 | 2024000078279 | 7/29/24 1:09 PM | 9 LINCOLN SQ Worcester, MA | Daniel Adjei | 1 | R. Empty Hand Compliance Techniques / Level 3 | Visible Injury | Neil F O'Connor | 7/29/24 4:27 PM | 1 |
1 | 2024000078796 | 7/30/24 4:38 PM | 6 SHANNON ST Apt #: 2 Worcester, MA | Rachel E Frisch | 3 | R. Empty Hand Compliance Techniques / Level 3 | _None | Michael A Cappabianca Jr | 7/30/24 6:57 PM | 2 |
2 | 2024000078796 | 7/30/24 4:38 PM | 6 SHANNON ST Apt #: 2 Worcester, MA | Michael Genese | 2 | R. Empty Hand Compliance Techniques / Level 3 | _None | Michael A Cappabianca Jr | 7/30/24 6:55 PM | 3 |
Time Trend of Incidents Over the Month of July¶
Question: What's the time trend of incidents over the month of July 2024?
We will create a
line plot
to see how incidents vary over time.plt.plot()
: The basic function to draw a line plot. Here, we specify the x and y data. The marker adds symbols at each data point (e.g., 'o' for circles), while linestyle defines how the line appears (e.g., solid or dashed).plt.title(), plt.xlabel(), plt.ylabel()
: These functions are used to set the title and axis labels. The fontsize parameter helps to adjust the font size for readability.plt.xticks()
: Used to rotate the labels on the x-axis to avoid overlapping when displaying dates.plt.grid()
: Adds a grid to the plot for better visual understanding of data trends.
# Convert 'Date___Time' column to datetime format
data['Date___Time'] = pd.to_datetime(data['Date___Time'], format='%m/%d/%y %I:%M %p')
# Create a new column for the date
data['Date'] = data['Date___Time'].dt.date
# Group data by date and count incidents per day
incidents_per_day = data.groupby('Date').size()
print(incidents_per_day.index)
print(incidents_per_day.values)
Index([2024-07-01, 2024-07-02, 2024-07-03, 2024-07-04, 2024-07-05, 2024-07-06, 2024-07-07, 2024-07-10, 2024-07-11, 2024-07-12, 2024-07-13, 2024-07-15, 2024-07-16, 2024-07-17, 2024-07-20, 2024-07-21, 2024-07-22, 2024-07-24, 2024-07-26, 2024-07-27, 2024-07-29, 2024-07-30, 2024-07-31], dtype='object', name='Date') [1 5 3 5 1 7 1 1 4 3 4 6 2 3 3 6 2 3 2 1 5 3 3]
# Convert 'Date___Time' column to datetime format
data['Date___Time'] = pd.to_datetime(data['Date___Time'], format='%m/%d/%y %I:%M %p')
# Create a new column for the date
data['Date'] = data['Date___Time'].dt.date
# Group data by date and count incidents per day
incidents_per_day = data.groupby('Date').size()
# Plotting the line plot
plt.figure(figsize=(10, 6))
plt.plot(incidents_per_day.index, incidents_per_day.values, marker='o', linestyle='-')
plt.title("Time Trend of Incidents in July 2024", fontsize=16)
plt.xlabel("Date", fontsize=12)
plt.ylabel("Number of Incidents", fontsize=12)
plt.xticks(rotation=45)
plt.grid(True)
plt.tight_layout()
plt.show()
Percentage Distribution of Injury vs. Non-Injury Incidents¶
Question: What's the percentage distribution of incidents involving injuries?
We'll use a
pie chart
to understand the proportion of incidentsinvolving injuries
versusno injuries
.plt.pie()
: This function generates a pie chart. Here are the key parameters:labels
: Labels for each slice of the pie (e.g., injury vs. non-injury).autopct='%1.1f%%'
: This formats the percentage display inside the slices.startangle=90
: Rotates the start of the pie chart to improve visual orientation.colors
: You can provide a list of colors to customize the pie chart.
plt.axis('equal')
: Ensures that the pie chart is drawn as a circle rather than an ellipse.
# Calculate the count of incidents with injury and without injury
injury_counts = data['Injury'].value_counts()
# Create a pie chart
plt.figure(figsize=(7, 7))
plt.pie(injury_counts, labels=injury_counts.index, autopct='%1.1f%%', startangle=90, colors=['#ff9999','#66b3ff'])
plt.title("Percentage Distribution of Injury vs. Non-Injury Incidents", fontsize=16)
plt.axis('equal') # Equal aspect ratio ensures that pie chart is drawn as a circle.
plt.show()
Most Frequently Used Techniques¶
Question: Which techniques were most frequently used in the incidents?
We’ll create a
bar plot
to identify which techniques were most frequently employed.plt.bar()
: This function creates a vertical bar plot.color='skyblue'
: Customizes the color of the bars.plt.xticks()
: Helps rotate and position the x-axis labels, ensuring they don’t overlap.plt.tight_layout()
: Ensures the plot and labels fit well within the figure area, avoiding overlap.
# Count occurrences of each use-of-force technique
technique_counts = data['Implement'].value_counts()
# Create a bar plot
plt.figure(figsize=(10, 6))
plt.bar(technique_counts.index, technique_counts.values, color='skyblue')
plt.title("Most Frequently Used Techniques", fontsize=16)
plt.xlabel("Technique", fontsize=12)
plt.ylabel("Frequency", fontsize=12)
plt.xticks(rotation=45, ha='right')
plt.tight_layout()
plt.show()
Top Locations for Incidents¶
Question: What are the top locations for incidents?
Using a
horizontal bar plot
, we will examine which locations had the most incidents.plt.barh()
: Creates a horizontal bar plot.- This is especially useful when your category labels (e.g., locations) are long and might overlap if displayed on the x-axis.
plt.barh(index, values)
: The index represents the labels (categories), and values are the frequencies or counts.color='lightcoral'
: Customizes the bar color to improve visual appeal.
# Count occurrences of incidents at each location
location_counts = data['Location'].value_counts()
# Sort the values in descending order and get the top 10 locations
location_counts = location_counts.sort_values(ascending=False).head(10)
# Reverse the order of the data
location_counts = location_counts.iloc[::-1]
# Create a horizontal bar plot
plt.figure(figsize=(10, 6))
plt.barh(location_counts.index, location_counts.values, color='lightcoral')
plt.title("Top 10 Locations for Incidents", fontsize=16)
plt.xlabel("Number of Incidents", fontsize=12)
plt.ylabel("Location", fontsize=12)
# Add annotation (text) on top of each bar
for i, value in enumerate(location_counts.values):
plt.text(value + 0.1, i, str(value), va='center', fontweight='bold')
plt.tight_layout()
plt.show()
Introduction to Seaborn¶
Seaborn
is a Python data visualization library built on top of Matplotlib
. It is specifically designed to make it easier to create aesthetically pleasing and informative statistical graphics.
Seaborn comes with high-level interfaces for drawing attractive and informative statistical graphics, making it a great tool for exploratory data analysis.
Key Features of Seaborn:
Simplified Syntax: Seaborn allows you to create complex visualizations with just a few lines of code.
Integration with Pandas: It works seamlessly with
Pandas
dataframes, allowing for easy plotting of complex datasets.Built-in Statistical Functions:
Seaborn
offers built-in functions to handle common statistical plots likeboxplots
,violin plots
, andheatmaps
.Aesthetic Plotting: Seaborn’s default settings create plots with modern, clean styles that are aesthetically pleasing without requiring additional customization.
Comparison Between Seaborn and Matplotlib¶
Feature | Matplotlib | Seaborn |
---|---|---|
Customization | Highly customizable but requires more effort | Built-in themes and styles for modern designs |
Ease of Use | Requires more code for complex plots | Simplified syntax for complex visualizations |
Default Styles | Simple and basic | Aesthetically pleasing by default |
Statistical Plotting | No built-in statistical functions | Built-in functions for statistical visualizations |
Integration with Pandas | Requires conversion or manual work | Integrates directly with Pandas dataframes |
Plot Types | Supports a wide range of plot types | Best suited for statistical plots |
import seaborn as sns
import pandas as pd
Exercise 01 lineplot: Time Trend of Incidents Over the Month of July (Seaborn)¶
sns.lineplot()
: This function simplifies the creation of a line plot with additional styling. Themarker='o'
adds markers at each data pointSmoother Design:
Seaborn
automatically applies a more polished design, including better line widths and colors.
data = pd.read_csv('F:/Clark_Universiy/Clark_Teaching/Git_Repo/ssj-302/docs/Lectures/data/Worcester_Police_Use_of_Force_Incidents_(July_2024).csv')
# Convert 'Date___Time' column to datetime format
data['Date___Time'] = pd.to_datetime(data['Date___Time'], format='%m/%d/%y %I:%M %p')
# Create a new column for the date
data['Date'] = data['Date___Time'].dt.date
# Group data by date and count incidents per day
incidents_per_day = data.groupby('Date').size()
incidents_per_day
--------------------------------------------------------------------------- NameError Traceback (most recent call last) Cell In[1], line 1 ----> 1 data = pd.read_csv('F:/Clark_Universiy/Clark_Teaching/Git_Repo/ssj-302/docs/Lectures/data/Worcester_Police_Use_of_Force_Incidents_(July_2024).csv') 3 # Convert 'Date___Time' column to datetime format 4 data['Date___Time'] = pd.to_datetime(data['Date___Time'], format='%m/%d/%y %I:%M %p') NameError: name 'pd' is not defined
# Create a line plot with Seaborn
plt.figure(figsize=(10, 6))
sns.lineplot(x=incidents_per_day.index, y=incidents_per_day.values, marker='o', color='royalblue')
# Customize the plot
plt.title("Time Trend of Incidents in July 2024", fontsize=16)
plt.xlabel("Date", fontsize=12)
plt.ylabel("Number of Incidents", fontsize=12)
plt.xticks(rotation=45)
plt.grid(True)
plt.tight_layout()
plt.show()
Plotly¶
!pip install plotly
Collecting plotly Downloading plotly-5.18.0-py3-none-any.whl (15.6 MB) Requirement already satisfied: packaging in g:\anaconda\lib\site-packages (from plotly) (20.1) Collecting tenacity>=6.2.0 Downloading tenacity-8.2.3-py3-none-any.whl (24 kB) Requirement already satisfied: six in g:\anaconda\lib\site-packages (from packaging->plotly) (1.14.0) Requirement already satisfied: pyparsing>=2.0.2 in g:\anaconda\lib\site-packages (from packaging->plotly) (2.4.6) Installing collected packages: tenacity, plotly Successfully installed plotly-5.18.0 tenacity-8.2.3
Plotly Express¶
Plotly Express is a high-level interface for creating interactive visualizations in Python. It simplifies the process of making complex, interactive plots by offering concise syntax.
## Scatter plots
import plotly.express as px
df = px.data.stocks()
print(df.head(5))
fig = px.line(df, x='date', y="GOOG")
fig.show()
date GOOG AAPL AMZN FB NFLX MSFT 0 2018-01-01 1.000000 1.000000 1.000000 1.000000 1.000000 1.000000 1 2018-01-08 1.018172 1.011943 1.061881 0.959968 1.053526 1.015988 2 2018-01-15 1.032008 1.019771 1.053240 0.970243 1.049860 1.020524 3 2018-01-22 1.066783 0.980057 1.140676 1.016858 1.307681 1.066561 4 2018-01-29 1.008773 0.917143 1.163374 1.018357 1.273537 1.040708
Exercise 02 Line plot: Time Trend of Incidents Over the Month of July (Plotly)¶
fig = px.line(x=incidents_per_day.index, y=incidents_per_day.values, labels={'x': 'Date', 'y': 'Incidents'},title='Interactive Time Series')
fig.show()
Bar charts with Plotly Express¶
data_canada = px.data.gapminder().query("country == 'Canada'")
fig = px.bar(data_canada, x='year', y='pop')
fig.show()
Exercise 03 Bar chart: Time Trend of Incidents Over the Month of July (Plotly)¶
fig = px.bar(x=data['Implement'].value_counts().index, y=data['Implement'].value_counts().values,
labels={'x': 'Implement', 'y': 'Count'},title='Implement')
fig
import plotly.graph_objects as go
trees = pd.read_csv('F:\\Clark_Universiy\\Clark_Teaching\\Git_Repo\\ssj-302\\docs\\Lectures\\Week06_visualization\\Street_Trees.csv')
valid_trees = trees[trees['dbh'] > 0]
valid_trees['text'] = 'Neighborhoods:' + valid_trees['neighborhood'] + ' ' + 'Tree:' + valid_trees['spp_bot'] + ' Diamemter:' + valid_trees['dbh'].astype(str)
g:\Anaconda\lib\site-packages\ipykernel_launcher.py:1: SettingWithCopyWarning: A value is trying to be set on a copy of a slice from a DataFrame. Try using .loc[row_indexer,col_indexer] = value instead See the caveats in the documentation: https://pandas.pydata.org/pandas-docs/stable/user_guide/indexing.html#returning-a-view-versus-a-copy
fig = go.Figure(data=go.Scattergeo(
lon = valid_trees['x_longitude'],
lat = valid_trees['y_latitude'],
text = valid_trees['text'],
mode = 'markers' ))
fig.update_layout(
title = 'Treekeeper Street Trees',
geo = dict(
scope = 'usa',
center=dict(lat=42.3601, lon=-71.0589),
projection_scale=50
),
width=1000, # Set figure width
height=600 # Set figure height
)
fig.show()