Analyze Data from the National Parks about Endangered Species

Analyze Data from the National Parks about Endangered Species
Analyze Data from the National Parks about Endangered Species

This goal of this project is to analyze biodiversity data from the National Parks Service, particularly around various species observed in different national park locations.

This project will scope, analyze, prepare, plot data, and seek to explain the findings from the analysis.

Scoping

It’s beneficial to create a project scope whenever a new project is being started. Four sections were created below to help guide the project’s process and progress. The first section is the project goals, this section will define the high-level objectives and set the intentions for this project. The next section is the data, luckily in this project, data is already provided but still needs to be checked if project goals can be met with the available data. Thirdly, the analysis will have to be thought through, which include the methods and questions that are aligned with the project goals. Lastly, evaluation will help us build conclusions and findings from our analysis.

Project Goals

In this project, the perspective will be through a biodiversity analyst for the National Parks Service. The National Park Service wants to ensure the survival of at-risk species, to maintain the level of biodiversity within their parks. Therefore, the main objectives as an analyst will be understanding characteristics about the species and their conservation status, and those species and their relationship to the national parks. Some questions that are posed:

  • What is the distribution of conservation status for species?
  • Are certain types of species more likely to be endangered?
  • Are the differences between species and their conservation status significant?
  • Which animal is most prevalent and what is their distribution amongst parks?

Data

This project has two data sets that came with the package. The first csv file has information about each species and another has observations of species with park locations. This data will be used to analyze the goals of the project.

Analysis

In this section, descriptive statistics and data visualization techniques will be employed to understand the data better. Statistical inference will also be used to test if the observed values are statistically significant. Some of the key metrics that will be computed include:

  1. Distributions
  2. counts
  3. relationship between species
  4. conservation status of species
  5. observations of species in parks.

Evaluation

Lastly, it’s a good idea to revisit the goals and check if the output of the analysis corresponds to the questions first set to be answered (in the goals section). This section will also reflect on what has been learned through the process, and if any of the questions were unable to be answered. This could also include limitations or if any of the analysis could have been done using different methodologies.

#First, import the primary modules that will be used in this project:
import pandas as pd
import numpy as np

from matplotlib import pyplot as plt
import seaborn as sns

%matplotlib inline

Loading the Data

To analyze the status of conservation of species and their observations in national parks, load the datasets into DataFrames. Once loaded as DataFrames the data can be explored and visualized with Python.

In the next steps, Observations.csv and Species_info.csv are read in as DataFrames called observations and species respectively. The newly created DataFrames are glimpsed with .head() to check its contents.

Species

The species_info.csv contains information on the different species in the National Parks. The columns in the data set include:

  • category – The category of taxonomy for each species
  • scientific_name – The scientific name of each species
  • common_names – The common names of each species
  • conservation_status – The species conservation status
species = pd.read_csv('species_info.csv',encoding='utf-8')
species.head()

Observations

The Observations.csv contains information from recorded sightings of different species throughout the national parks in the past 7 days. The columns included are:

  • scientific_name – The scientific name of each species
  • park_name – The name of the national park
  • observations – The number of observations in the past 7 days
observations = pd.read_csv('observations.csv', encoding='utf-8')
observations.head()

Explore the Data

It is time to explore the species data a little more in depth. The first thing is to find the number of distinct species in the data. Use the column scientific_name to get 5,541 unique species. There seems to be a lot of species in the national parks!

print(f"number of species:{species.scientific_name.nunique()}")
print(f"nnumber of categories:{species.category.nunique()}")
print(f"categories:{species.category.unique()}")
number of species:5541
nnumber of categories:7
categories:['Mammal' 'Bird' 'Reptile' 'Amphibian' 'Fish' 'Vascular Plant'  'Nonvascular Plant']

Here is a chance to drill one level deeper and see the count of category in the data. Vascular plants are by far the largest share of species with 4,470 in the data with reptiles being the fewest with 79.

species.groupby("category").size()
category
Amphibian              80
Bird                  521
Fish                  127
Mammal                214
Nonvascular Plant     333
Reptile                79
Vascular Plant       4470
dtype: int64

Another column to explore is conservation_status. The column has 4 categories, Species of ConcernEndangeredThreatenedIn Recovery, and nan values.

print(f"number of conservation statuses:{species.conservation_status.nunique()}")
print(f"unique conservation statuses:{species.conservation_status.unique()}")
print(f"na values:{species.conservation_status.isna().sum()}")

print(species.groupby("conservation_status").size())
number of conservation statuses:4
unique conservation statuses:[nan 'Species of Concern' 'Endangered' 'Threatened' 'In Recovery']
na values:5633
conservation_status
Endangered             16
In Recovery             4
Species of Concern    161
Threatened             10
dtype: int64

Observations

The next section looks at observations data. The first task is to check the number of parks that are in the dataset and there are only 4 national parks.

print(f"number of parks:{observations.park_name.nunique()}")
print(f"unique parks:{observations.park_name.unique()}")
print(f"number of observations:{observations.observations.sum()}")
number of parks:4
unique parks:['Great Smoky Mountains National Park' 'Yosemite National Park'
 'Bryce National Park' 'Yellowstone National Park']
number of observations:3314739

Analysis

This section will begin analyzing the data after the initial exploration. First task will be to clean and explore the conservation_status column in species.

The column conservation_status has several possible values:

  • Species of Concern: declining or appear to be in need of conservation
  • Threatened: vulnerable to endangerment in the near future
  • Endangered: seriously at risk of extinction
  • In Recovery: formerly Endangered, but currently neither in danger of extinction throughout all or a significant portion of its range

In the exploration, a lot of nan values were detected. These values will need to be converted to No Intervention.

species.fillna('No Intervention', inplace=True)
species.groupby("conservation_status").size()
conservation_status
Endangered              16
In Recovery              4
No Intervention       5633
Species of Concern     161
Threatened              10
dtype: int64

Next is to checkout the different categories that are nested in the conservation_status column except for the ones that do not require an intervention. There is both the table and chart to explore below.

For those in the Endangered status, 7 were mammals and 4 were birds. In the In Recovery status, there were 3 birds and 1 mammal, which could possibly mean that the birds are bouncing back more than the mammals.

conservationCategory = species[species.conservation_status != "No Intervention"]\
    .groupby(["conservation_status", "category"])['scientific_name']\
    .count()\
    .unstack()

conservationCategory
ax = conservationCategory.plot(kind = 'bar', figsize=(8,6), 
                               stacked=True)
ax.set_xlabel("Conservation Status")
ax.set_ylabel("Number of Species");

In conservation

The next question is if certain types of species are more likely to be endangered? This can be answered by creating a new column called is_protected and include any species that had a value other than No Intervention.

species['is_protected'] = species.conservation_status != 'No Intervention'

Once the new column is created, group by category and is_protected to show the break down of each species type and protection status.

It’s easy to see that Birds, Vascular Plants, and Mammals have a higher absolute number of species protected.

category_counts = species.groupby(['category', 'is_protected'])\
                        .scientific_name.nunique()\
                        .reset_index()\
                        .pivot(columns='is_protected',
                                      index='category',
                                      values='scientific_name')\
                        .reset_index()
category_counts.columns = ['category', 'not_protected', 'protected']

category_counts

Absolute numbers are not always the most useful statistic, therefore it’s important to calculate the rate of protection that each category exhibits in the data. From this analysis, one can see that ~17 percent of mammals were under protection, as well as ~15 percent of birds.

category_counts['percent_protected'] = category_counts.protected / \
                                      (category_counts.protected + category_counts.not_protected) * 100

category_counts

Statistical Significance

This section will run some chi-squared tests to see if different species have statistically significant differences in conservation status rates. In order to run a chi squared test, a contingency table will need to be created. The contingency table should look like this:

protectednot protected
Mammal??
Bird??

The first test will be called contingency1 and will need to be filled with the correct numbers for mammals and birds.

The results from the chi-squared test returns many values, the second value which is 0.69 is the p-value. The standard p-value to test statistical significance is 0.05. For the value retrieved from this test, the value of 0.69 is much larger than 0.05. In the case of mammals and birds there doesn’t seem to be any significant relationship between them i.e. the variables independent.

from scipy.stats import chi2_contingency

contingency1 = [[30, 146],
              [75, 413]]
chi2_contingency(contingency1)
(0.1617014831654557,
 0.6875948096661336,
 1,
 array([[ 27.8313253, 148.1686747],
        [ 77.1686747, 410.8313253]]))

The next pair, is going to test the difference between Reptile and Mammal.

The format is again is like below:

protectednot protected
Mammal??
Reptile??

This time the p-value is 0.039 which is below the standard threshold of 0.05 which can be take that the difference between reptile and mammal is statistically significant. Mammals are shown to have a statistically significant higher rate of needed protection compared with Reptiles.

contingency2 = [[30, 146],
               [5, 73]]
chi2_contingency(contingency2)
(4.289183096203645,
 0.03835559022969898,
 1,
 array([[ 24.2519685, 151.7480315],
        [ 10.7480315,  67.2519685]]))

Species in Parks

The next set of analysis will come from data from the conservationists as they have been recording sightings of different species at several national parks for the past 7 days.

The first step is to look at the the common names from species to get an idea of the most prevalent animals in the dataset. The data will be need to be split up into individual names.

from itertools import chain
import string

def remove_punctuations(text):
    for punctuation in string.punctuation:
        text = text.replace(punctuation, '')
    return text

common_Names = species[species.category == "Mammal"]\
    .common_names\
    .apply(remove_punctuations)\
    .str.split().tolist()

common_Names[:6]
[['Gappers', 'RedBacked', 'Vole'],
 ['American', 'Bison', 'Bison'],
 ['Aurochs',
  'Aurochs',
  'Domestic',
  'Cattle',
  'Feral',
  'Domesticated',
  'Cattle'],
 ['Domestic', 'Sheep', 'Mouflon', 'Red', 'Sheep', 'Sheep', 'Feral'],
 ['Wapiti', 'Or', 'Elk'],
 ['WhiteTailed', 'Deer']]

The next step is to clean up duplicate words in each row since they should no be counted more than once per species.

cleanRows = []

for item in common_Names:
    item = list(dict.fromkeys(item))
    cleanRows.append(item)
    
cleanRows[:6]
[['Gappers', 'RedBacked', 'Vole'],
 ['American', 'Bison'],
 ['Aurochs', 'Domestic', 'Cattle', 'Feral', 'Domesticated'],
 ['Domestic', 'Sheep', 'Mouflon', 'Red', 'Feral'],
 ['Wapiti', 'Or', 'Elk'],
 ['WhiteTailed', 'Deer']]

Next the words need to be collapsed into one list for easier use.

res = list(chain.from_iterable(i if isinstance(i, list) else [i] for i in cleanRows))
res[:6]
['Gappers', 'RedBacked', 'Vole', 'American', 'Bison', 'Aurochs']

Now the data is ready to be able to count the number of occurrences of each word. From this analysis, it seems that Bat occurred 23 times while Shrew came up 18 times.

words_counted = []

for i in res:
    x = res.count(i)
    words_counted.append((i,x))

pd.DataFrame(set(words_counted), columns =['Word', 'Count']).sort_values("Count", ascending = False).head(10)

In the data, there are several different scientific names for different types of bats. The next task is to figure out which rows of species are referring to bats. A new column made up of boolean values will be created to check if is_bat is True.

species['is_bat'] = species.common_names.str.contains(r"\bBat\b", regex = True)

species.head(10)

Here is a subset of the data where is_bat is true, returning see the rows that matched. There seems to be a lot of species of bats and a mix of protected vs. non-protected species.

species[species.is_bat]

Next the results of the bat species will be merged with observations to create a DataFrame with observations of bats across the four national parks.

bat_observations = observations.merge(species[species.is_bat])
bat_observations

Let’s see how many total bat observations(across all species) were made at each national park.

The total number of bats observed in each park over the past 7 days are in the table below. Yellowstone National Park seems to have the largest with 8,362 observations and the Great Smoky Mountains National Park having the lowest with 2,411.

bat_observations.groupby('park_name').observations.sum().reset_index()

Now let’s see each park broken down by protected bats vs. non-protected bat sightings. It seems that every park except for the Great Smoky Mountains National Park has more sightings of protected bats than not. This could be considered a great sign for bats.

obs_by_park = bat_observations.groupby(['park_name', 'is_protected']).observations.sum().reset_index()
obs_by_park

Below is a plot from the output of the last data manipulation. From this chart one can see that Yellowstone and Bryce National Parks seem to be doing a great job with their bat populations since there are more sightings of protected bats compared to non-protected species. The Great Smoky Mountains National Park might need to beef up there efforts in conservation as they have seen more non-protected species.

plt.figure(figsize=(16, 4))
sns.barplot(x=obs_by_park.park_name, y= obs_by_park.observations, hue=obs_by_park.is_protected)
plt.xlabel('National Parks')
plt.ylabel('Number of Observations')
plt.title('Observations of Bats per Week')
plt.show()

Conclusions

The project was able to make several data visualizations and inferences about the various species in four of the National Parks that comprised this data set.

This project was also able to answer some of the questions first posed in the beginning:

  • What is the distribution of conservation status for species?
    • The vast majority of species were not part of conservation.(5,633 vs 191)
  • Are certain types of species more likely to be endangered?
    • Mammals and Birds had the highest percentage of being in protection.
  • Are the differences between species and their conservation status significant?
    • While mammals and Birds did not have significant difference in conservation percentage, mammals and reptiles exhibited a statistically significant difference.
  • Which animal is most prevalent and what is their distribution amongst parks?
    • the study found that bats occurred the most number of times and they were most likely to be found in Yellowstone National Park.

Leave a Reply

Prev
Types of Missing Data in Data Science
Types of Missing Data in Data Science

Types of Missing Data in Data Science

Missing data is a common problem in data analysis that can have a significant

Next
Linear Algebra with Python – Tutorial for Beginners
Linear Algebra with Python - Tutorial for Beginners

Linear Algebra with Python – Tutorial for Beginners

Linear Algebra with Python is an essential tool for scientific computing, data

You May Also Like