Project #4: United States Congress
In this DISCOVERY Data Science Project, you will do real data science in less than an hour and you will earn this project's card to your collection when you fully complete this project! 🎉
Data Source: @unitedstates' Current Members of Congress
The @unitedstates GitHub organization described itself as a "shared commons of data and tools for the United States. Made by the public, used by the public." Their most popular work is the congress-legislators collection of data, which contains every member "of the United States Congress (1789-Present), congressional committees (1973-Present), committee membership (current only), and presidents and vice presidents of the United States in YAML, JSON, and CSV format."
The dataset of currently serving Members of Congress, known as the legislators-current dataset, is in CSV format at https://unitedstates.github.io/congress-legislators/legislators-current.csv.
In this MicroProject, you'll both explore working with the date/time "data type" or "dtype" in pandas and create several exploratory data visualizations! Let's nerd out! 🎉
Background Knowledge
To finish this MicroProject, we assume you already know:
- All topics covered in DISCOVERY Module 1: Basics of Data Science with Python (review the module here)
- Adding new rows and columns into an existing DataFrame (review creating new columns here)
Let's get started! :)
Part 1: Importing the legislators-current dataset
Create a DataFrame in the Python variable named df that contains the legislators-current data located at the following URL:
https://unitedstates.github.io/congress-legislators/legislators-current.csv
Understanding the Truncation of Rows and Columns in a DataFrame's Output
To save space, pandas will default to showing a truncated list of rows and columns by using the ... to skip over columns when there are too many columns.
The pandas library provides over a hundred different configurable options for DataFrames that you, a data scientist, can control using the pd.get_option("option.name") and pd.set_option("option.name", value) functions.
The option that controls the maximum number of columns that are visible before Python begins to truncate your output is named display.max_columns.
Run the code below to see what the current, default value for display.max_columns:
Updating the display.max_columns Value
After finding the default option, change the number of max_columns displayed in this notebook to 50 columns (instead of the default 20 columns) by using the set_option command explained in the previous section:
In addition to setting the maximum columns to 50, you can feel free to change:
- the number of rows using
"display.max_rows"(default is60) - ...or most any of the options available at in the pandas documentation for
set_option.
Display the DataFrame
With the updated display.max_columns value, display the DataFrame again and note that all columns are visible by scrolling right to view the entire DataFrame:
⚙️ Test Case: Part 1: Importing the legislators-current dataset
Part 2: Working with Dates in pandas via Congressional Birthdays
One of the trickiest things to work with in data science are dates. One of the first challenges is that dates can be represented in many, many ways:
- In the United States, December 13, 1989 is commonly written as
12/13/1989,12/13/89, or12-13-89. Each of these contain the month, then day, and then year. - In Europe, the same December 13, 1989 is commonly written as
13/12/1989,13/12/89or13-12-1989. Instead of listing the month first, the day is listed first, then month, and then year. - In many other places in the world, the same December 13, 1989 is written as
1989/12/13or1989-12-13. This format lists the year first, then month, and then day.
In 1988, a group of computer scientists met together and published an international standard for the "worldwide exchange and communication of date and time-related data". This standard is referred to as ISO 8601 and is the universal format to easily work with dates as data.
The ISO 8601 standard says that dates must be encoded in only one of two formats:
- Option #1:
YYYY-MM-DD - Option #2:
YYYYMMDD(same order, but without the-)
Let's look at the birthday column in the dataset and see if this standard is followed in our dataset:
Part 2.1: Converting an ISO formatted date into a DateTime data type
At the bottom of the output above, pandas will report the datatype -- or dtype -- of the data is an object:
Example:
... Name: birthday, Length: 539, dtype: object
In general, there are three major data types commonly used in a DataFrame:
- Numbers, commonly with a dtype of
int64(whole numbers) orfloat64(numbers with decimals), - Strings, commonly with a dtype of
object, AND - Date/Time, commonly with a dtype of
datetime64[ns]
When the dtype of a column is a number, we can perform numerical operations on the data (ex: sum, mean, etc). When the dtype of the data is a string, we can perform string operations on the data (ex: contains, etc). Finally, when the dtype is a Date/Time, we can perform datetime operations on it (ex: month, day, etc).
To convert data from a string to a date, the pandas library provides the function:
pd.to_datetime( df["column_with_iso_date_strings"] )
Create a new column in your DataFrame df called birthday_dt that stores the birthday date as a DateTime:
The data should look the same, but the dtype will be updated to datetime64[ns] instead of object.
⚙️ Test Case: Part 2.1: Converting an ISO formatted date into a DateTime data type
Part 2.2: Using DateTime Operations
When your data in a DataFrame is the dtype of datetime64[ns], we are able to access date-specific functions for the column by using the .dt functions. Several common functions we can now use on the birthday_dt column include:
df["birthday_dt"].dt.month # Returns the numeric month; ex: 1 for January through 12 for December
df["birthday_dt"].dt.day # Returns the numeric day of the month; ex: 13 for Dec. 13, 1989 (`1989-12-13`)
df["birthday_dt"].dt.year # Returns the numeric year; ex: 1989
df["birthday_dt"].dt.dayofweek # Returns a numeric day of the week; 0 for Monday through 6 for Sunday
df["birthday_dt"].dt.hour # Returns the numeric hour based on a 24-hour clock
df["birthday_dt"].dt.minute # Returns the numeric minute
df["birthday_dt"].dt.second # Returns the numeric second
# ..and several others...Finding Birthdays
When every seat in Congress is filled, there are a total of 541 members (100 senators, 435 voting representatives, and 6 non-voting representatives from non-voting districts and territories). Since there are at most 366 days in a leap year, is it always someone's birthday in Congress?
To nerd out with some exploratory data analysis to answer this question, we will graph every day of the year as a scatter plot with x-axis containing the day of birth of each member of congress and the y-axis containing the month of birth of each member of Congress.
To create this scatter plot, we need both pieces of data in individual columns in our DataFrame:
- Add a new column to
dfcalleddayOfBirththat contains the day of birth for the member of Congress - Add a new column to
dfcalledmonthOfBirththat contains the month of birth for the member of Congress
⚙️ Test Case: Part 2.2: Using DateTime Operations
Part 2.3: Create a Scatter Plot
Using df.plot.scatter, create a scatter plot with the x-axis data being the day of birth of each member of Congress and the y-axis data being the month of birth of each member of Congress:
(If you need a refresher, the DISCOVERY page "Basic Data Visualization in Python" steps you through the code to build a scatter plot!)
Analysis: A Day Without a Birthday Celebration?
The scatter plot of days/months of birth is an example of an exploratory data visualization. This visualization alone does not provide much context about the data itself and may be difficult for others to immediately understand, but it helps a data scientist understand the data to ask additional questions.
Using your exploratory data visualization, write a date in ISO format that no member of Congress will have a birthday (using the current year as the year) and store that date in the Python variable noCongressBirthdaysCelebrated:
⚙️ Test Case: Part 2.3: A Day Without a Birthday Celebration
Part 2.4: Showing Popular Birthdays
In addition to providing the x and y values to the df.plot.scatter function, you can provide an alpha value to your scatter plot to make the data points partially transparent to see where there are multiple data points at a single location.
Create another scatter plot of the same data, adding to the function the option alpha = 0.2 to make each data point 80% transparent. (Feel free to play with the alpha value to make it exactly how you like it!)
Analysis: A Popular Day for Birthdays
Most days of the year have a single member of Congress with a birthday on that day. In your exploratory data visualization with alpha transparency, days with only a single birthday will visually show as a very pale dot.
When two members of Congress have a birthday on the same day, the two partially transparent dots are drawn on top of each other making the data point darker.
Write a date in ISO format for a date that at least four members of congress will celebrate their birthday together (using the current year as the year) and store it in the variable popularCongressBirthday:
⚙️ Test Case: Part 2.4: A Popular Day for Birthdays
Part 3: The House of Representatives and The Senate
There's one final question about birthdays -- are senators or representatives older?
Part 3.1: Calculating the Age of Each Member of Congress
One unique features of the datetime dtype is that, just like numeric data, you can add and subtract dates! Unlike numeric data, however, the difference between two dates is a span of time called a "time delta". In the cell below, we calculate the "time delta" from the current moment in time (datetime.datetime.now()) to the time that each member of Congress was born:
By default, the largest unit of time that Python natively understands is "1 day" (exactly 86,400 seconds). A more meaningful measure for us, as humans, is to represent someone's age in terms of years instead of days.
In Python, when you have a "time delta", we can divide by another time delta to get a numeric value.
In the following cell, we divide the previous calculation by 365.25 days (an very close approximation for the number of days in a year, accounting for leap years):
Assign the calculation we did above to a new column in the DataFrame named age to store each member's age:
⚙️ Test Case: Part 3.1: Calculating the Age of Each Member of Congress
Part 3.2: Senators vs. Representatives
In this dataset, each row is one person who is a member of the United States Congress. The United States Congress is bicameral, being made up of both the House of Representatives and the Senate. In the dataset:
- Representatives in the House of Representatives represent a district, which is encoded in the
districtcolumn. Theirtypecolumn contains the string"rep". - Senators are elected for six years, with one-third of the senators up for election every two years. The
senate_classdenotes which election class a senator is in and when they are up for election. A senator'stypecolumn contains the string"sen".
In the following two cells, create two new DataFrames, df_house and df_senate, that contain members from only the House of Representatives (df_house) and the Senate (df_senate):
🔬 MicroProject Checkpoint Tests 🔬
⚙️ Test Case: Part 3.2: Senators vs. Representatives
Part 3.3: Box Plots
Finally, let's visually answer the question: are senators or representatives older?
To create a box plot to view the distribution of ages, we can use df.plot.box(...). The three options we absolutely need to use to create a box plot include:
by="type", to inform pandas to create a different box for each "type" of member of Congress (senators and representatives)column="age", to inform pandas to use theagecolumn when computing the Q1, Q2, Q3, and outlierstitle="Current Age of Members of Congress, by Chamber", to provide a descriptive title
Additionally, you may want to optionally add any, all, or none of the following options:
vert=False, if you prefer a horizontal box plotgrid=True, if you prefer gridlines on your box plotfigsize=(6, 4), to resize the shape of your plot (adjusting6and4as necessary)
Box Plot Analysis
The box plot shows the distribution of every member of Congress is a single visualization. From this, you can answer a lot of questions:
- Is the average median age of a representatives or senators older?
- Is the youngest representative older than the youngest senator? How old is that person?
- Is the oldest representative younger than the oldest senator? How old is that person?
- What's the oldest age you can be and still be younger than the half of all senators in the Senate?
- ... and many other questions, all from one visualization! :)
Validate and Complete This Project!
Congratulations on finishing this DISCOVERY Data Science Project! 🎉🎉
To validate your entire project, your entire code will run from top-to-bottom on this page and each test case will be validated one final time. If everything looks good, you'll earn the card for completing this project: