Have you ever wondered what kind of toxic chemicals are found in your every day beauty products? This jupyter notebook attempts to identify the number and type of chemicals most commonly found in Californian cosmetic products.
Since 2005, the California Safe Cosmetic Act requires all cosmetic companies with products sold in California to provide to the California Safe Cosmetics Program (CSCP) a list of their products suspected to cause cancer, birth defects, or other reproductive harm. CSCP has made this information available to the public, and the data can be downloaded from California Department of Public Health's website: https://healthdata.gov/dataset/chemicals-cosmetics/resource/d53946d0-adac-4a5a-a59e-dc6926604e5b
The original data contains 113,837 entries from 2005 to 2020. I have taken a sample of the 1,000 most recent entries last modified by the cosmetic manufacturer, packer, or distributer for the purpose of this lab. The sample data can be downloaded at: https://drive.google.com/file/d/1ZwwyNQI6h5L7Pisni2k3cZ5sGPU4iT3R/view?usp=sharing
# let's begin by importing the basic librariesimport pandas as pdimport numpy as nbimport matplotlib.pyplot as pltimport csvimport seaborn as sns%matplotlib inline# enable seaborn stylessns.set()sns.set_style('whitegrid')# read the .csv file and turn it into a normal Pandas Data Framecosmetics = pd.read_csv('/Users/janetliu/Desktop/cosmetics/cscpopendata-openrefinesample.csv')# we can check the first 5 rows to see what our data looks likecosmetics.head(5)# i am going to do a quick search to make sure i have uploaded the sample dataprint('Total reports: ' + str(len(cosmetics)))Total reports: 1000# the next step is to check our data type objects and see what our values are listed undercosmetics.dtypesCDPHId int64ProductName objectCSFId float64CSF objectCompanyId int64CompanyName objectBrandName objectPrimaryCategoryId int64PrimaryCategory objectSubCategoryId int64SubCategory objectCasId int64CasNumber objectChemicalId int64ChemicalName objectInitialDateReported objectMostRecentDateReported objectDiscontinuedDate float64ChemicalCreatedAt objectChemicalUpdatedAt objectChemicalDateRemoved objectChemicalCount int64dtype: object# most of the data we need are listed as objects## lets change the values into a date time formatcosmetics['InitialDateReported'] = pd.to_datetime(cosmetics['InitialDateReported'])cosmetics['MostRecentDateReported'] = pd.to_datetime(cosmetics['MostRecentDateReported'])cosmetics['DiscontinuedDate'] = pd.to_datetime(cosmetics['DiscontinuedDate'])cosmetics['MostRecentDateReported'] = pd.to_datetime(cosmetics['MostRecentDateReported'])cosmetics['DiscontinuedDate'] = pd.to_datetime(cosmetics['DiscontinuedDate'])cosmetics['ChemicalCreatedAt'] = pd.to_datetime(cosmetics['ChemicalCreatedAt'])cosmetics['ChemicalUpdatedAt'] = pd.to_datetime(cosmetics['ChemicalUpdatedAt'])cosmetics['ChemicalDateRemoved'] = pd.to_datetime(cosmetics['ChemicalDateRemoved'])### and categoriescosmetics.dtypesfor col in [ 'CompanyName','BrandName', 'PrimaryCategory', 'SubCategory', 'ChemicalName']: cosmetics[col] = cosmetics[col].astype('category')# then re-print the datatypes to make sure the changes were madecosmetics.dtypesCDPHId int64ProductName objectCSFId float64CSF objectCompanyId int64CompanyName categoryBrandName categoryPrimaryCategoryId int64PrimaryCategory categorySubCategoryId int64SubCategory categoryCasId int64CasNumber objectChemicalId int64ChemicalName categoryInitialDateReported datetime64[ns]MostRecentDateReported datetime64[ns]DiscontinuedDate datetime64[ns]ChemicalCreatedAt datetime64[ns]ChemicalUpdatedAt datetime64[ns]ChemicalDateRemoved datetime64[ns]ChemicalCount int64dtype: object# now that our values are in the right format, we can begin to answer the questions, ###'Who are the top 10 cosmetic brands with the most reported chemicals in their products?' and #### 'What are the top 10 toxic chemicals most commonly found in beauty products?'##### i hypothesize that high end, expensive brands will contain less toxic chemicals than cheaper, drugstore brands###### let's perform a value_counts on BrandName to answer the first questioncosmetics.BrandName.value_counts()[:10].plot(kind='bar', color='palevioletred') # value_count to show the frequency, and bar for barplot plt.xticks(rotation=-45) # set the x label angle plt.title('Top 10 Cosmetic Brands with the Highest Number of Reported Chemicals') #give a titleplt.ylabel('Chemical Count') #and label namesplt.xlabel('Cosmetic Brands')plt.rcParams['figure.figsize'] = figsize=(20,7) # note that altering rcParams can alter the inline graph size# the plot shows 'tarte', 'sephora' and 'DND' are the three top cosmetic brands with the most reported chemicals## this is alarming as i own cosmetic products from 5 brands listed on here### this plot also shows high end, expensive brands are equally likely to contain the same number of toxic chemicals#### what's also disturbing is that there is only one nail polish brand (DND) in the top 10##### while the rest are brands that sell face products such as innisfree, a brand i routinely use for their face masks###### let's perform another value_counts on ChemicalName to answer our next questioncosmetics.ChemicalName.value_counts()[:10].plot(kind='bar', color='palevioletred')plt.xticks(rotation=-90)plt.title('Top 10 Most Common Chemicals found in Cosmetic Products')plt.ylabel('Chemical Count')plt.xlabel('Chemical Name')plt.rcParams['figure.figsize'] = figsize=(20,7) # Of the 1,000 entries, the most common chemical found in cosmetic products is called 'Titanium dioxide'## which can also be found in paints, plastics, and rubber### 'taic' is also commonly found in rubber, and 'carbon black' is found in coal#### i am also curious to know the frequency of the latest chemicals reported in 2020### let's do this by creating a frequency timelinecosmetics.MostRecentDateReported.value_counts().plot(kind='line',color='palevioletred') #set to linepot plt.title('Timeline of most recent Chemicals reported in 2020')plt.ylabel('Number of reports')plt.xlabel('Date')plt.rcParams['figure.figsize'] = (20,7) # the graph shows there were over 175 reports made in late February, while the number of reports are declining## let's move on to seaborn for our next visualizations# for the second part, i want to find out, 'Which everyday cosmetic products include the most number of chemicals?'## and 'How many chemicals can be found in a cosmetic product?'### to answer the first question, let's create a count plot sns.catplot( #run a categorical plot with seaborn y="PrimaryCategory", kind="count", #use a countplot to find the frequency of reported chemicals height=7, #make the plot 7 units high palette='pastel', aspect=2, #height should be two times width data=cosmetics);plt.title('Number of Chemicals found in Everyday Cosmetic Products')plt.xlabel('Number of Chemicals')plt.ylabel('Types of Cosmetic Products')# the count plot shows 'make up products (non-permanent)' contains the most reported number of chemicals## but it is unclear what 'make up products' include### let's create another countplot to see the subcategories in this groupsns.catplot( #set up categorical scatterplots for each subcategory y="SubCategory", col="PrimaryCategory", color='palevioletred', col_wrap=2, #set the number of charts per row data=cosmetics, kind="count", height=6, aspect=1.5)# showing the number of subcategories within each primary category gives me a sense of what kind of ## every day beauty products are found in 'make up products (non-permanent)''### these include lip colors, eye shadows, face powders, and blushes which are products i should be more mindful of#### and read the ingredients when purchasing #### lastly, i want to create a violin chart to visualize the distribution of chemicals for each brand##### i can do this by looking at the total number of current chemicals reported for each brand's productssns.catplot( x="BrandName", y="ChemicalCount", kind="violin", palette='pastel', dpi=300, inner="stick", aspect=1.5, data=cosmetics);plt.title('Which beauty products contain the most chemicals?')plt.xlabel('Cosmetic Brands')plt.ylabel('Chemical Count in each Product')plt.xticks(rotation=-90) #rotate x-labelsfig = plt.gcf()fig.set_size_inches(20, 10)# this violin graph shows me a good idea of the distribution of chemicals found in each brand## Brands such as 'DND' and 'I'm Meme' have a greater violin shape that represents the greater number of 1 to 2 ### chemicals in their products.#### but it is more worrying to see brands such as 'Careline' that has such a large violin range, with some##### of their products containing more than 8 toxic chemicals in one productIt is easy to buy products from well-known cosmetic brands and assume they are safe. This jupyter notebook aims to point out the number of toxic chemicals that can be found in the every day products or brands that you know of or even own, that can help us make more informed decisions when we make our next purchase.