本文选取了四个经典案例,主要聚焦Pandas在数据分析中的简单应用,结合代码学习利用Python进行数据分析过程(●ˇ∀ˇ●)。在每个例题开始前,我们将会标注出本例题涉及的重要知识点,并在重要处添加解释和代码注释,共读者参考。
如果你遇到任何问题,欢迎在评论区一起讨论╰(*°▽°*)╯
点击最上方横幅下载本文例子中用到的数据包,或点此下载链接
敬告:数据内容仅供学习使用,不代表任何真实数据!请勿作他用!╰(‵□′)╯
Pandas(Python Data Analysis Library)是基于NumPy的数据分析模块,它提供了大量标准数据模型和高效操作大型数据集所需的工具。可以说Pandas是使得Python能够成为高效且强大的数据分析环境的重要因素之一。
Pandas提供了两种主要的数据结构:Series和DataFrame。这些数据结构使得在Python中进行数据操作和分析变得更加方便。
1. Series: Series是Pandas中的一维标记数组。它类似于一维数组或列表,但附带了标签(label),使得数据可以按标签进行访问和操作。Series由两部分组成:数据部分和索引(index)。数据部分存储了一组值,而索引是用于标识和访问这些值的标签。
例如,创建一个Series对象来表示学生的分数:
import pandas as pd
scores = pd.Series([85, 90, 75, 80])
这将创建一个包含分数数据的Series对象。默认情况下,索引将从0开始自动分配。可以通过指定索引来自定义标签:
scores = pd.Series([85, 90, 75, 80], index=['Alice', 'Bob', 'Charlie', 'Dave'])
现在,每个分数都与对应的学生姓名相关联。
2. DataFrame: DataFrame是Pandas中最常用的数据结构,它类似于一个二维表格或电子表格。它由行和列组成,每一列可以包含不同类型的数据(如数字、字符串、布尔值等)。
可以将DataFrame视为一组Series对象的集合,它们共享相同的索引。DataFrame提供了许多功能,包括数据选择、过滤、排序、统计和数据可视化等。
创建一个简单的DataFrame示例:
data = {'Name': ['Alice', 'Bob', 'Charlie', 'Dave'],
'Age': [25, 30, 35, 40],
'City': ['New York', 'London', 'Paris', 'Tokyo']}
df = pd.DataFrame(data)
将创建一个包含姓名、年龄和城市的DataFrame对象。每一列都表示一个Series对象,而DataFrame提供了将它们组合在一起的结构。
(≧∇≦)ノ学习更多关于Pandas的知识,请跳转到:Pandas教程(非常详细)-CSDN博客
GroupLens实验室提供了一些从MoviesLens用户那里收集的20世纪90年代末到21世纪初的电影评分数据的集合。浙西额数据提供了电影的评分、流派、年份和观众数据(年龄、邮编、性别、职业)。MovisLens1M数据集包含6000个用户对4000部电影的100万个评分。数据分布在三个表格之中:分别包含评分、用户信息和电影信息。
接下来我们将结合代码共同学习这一分析过程。本例题涉及以下知识点,重要处会有解释:
数据合并和连接:使用pd.merge()
函数将多个DataFrame
对象合并为一个新的DataFrame
,根据共同的列进行连接。通过合并数据,可以将不同来源的数据整合在一起,以便进行综合分析。
数据透视表:使用pivot_table()
函数创建数据透视表,基于不同的维度对数据进行汇总和聚合。
数据分组和聚合:使用groupby()
函数对DataFrame
对象进行分组,并应用聚合函数(如平均值、计数等)对每个组进行计算。通过分组和聚合,可以对数据进行更详细的分析和统计。
数据排序和筛选:使用sort_values()
函数对DataFrame
对象进行排序,根据特定的列进行升序或降序排序。同时,使用条件语句进行筛选,选择符合特定条件的行或列数据。
数据操作和转换:使用字符串处理函数(如split()
、explode()
等)对字符串类型的列进行操作和转换,以提取有用的信息或将数据拆分成多个行。这些操作可以使数据更加便于分析和理解。
数据统计和计算:使用聚合函数(如mean()
、std()
等)对数据进行统计和计算,揭示数据的统计特征和变化情况。
import pandas as pd
#文件皆位于根目录下
# 读取"users.dat"文件,它将列名设置为unames,并将分隔符设置为::。header=None参数表示文件中没有标题行,因此应使用unames中提供的列名。
unames = ["user_id", "gender", "age", "occupation", "zip"]
users = pd.read_table("datasets/movielens/users.dat", sep="::", header=None, names=unames, engine="python")
# 读取"ratings.dat"文件
rnames = ["user_id", "movie_id", "rating", "timestamp"]
ratings = pd.read_table("datasets/movielens/ratings.dat", sep="::", header=None, names=rnames, engine="python")
# 读取"movies.dat"文件
mnames = ["movie_id", "title", "genres"]
movies = pd.read_table("datasets/movielens/movies.dat", sep="::", header=None, names=mnames, engine="python")
#这段代码片段读取了三个文件("users.dat"、"ratings.dat"和"movies.dat"),
#并将它们分别存储在不同的pandas DataFrame中(users、ratings和movies),以便进一步分析或处理。
users.head(5)#输出用户DataFrame的前5行数据
ratings.head(5)# 输出评分DataFrame的前5行数据
movies.head(5)# 输出电影DataFrame的前5行数据
ratings# 输出完整的ratings DataFrame
user_id | movie_id | rating | timestamp | |
---|---|---|---|---|
0 | 1 | 1193 | 5 | 978300760 |
1 | 1 | 661 | 3 | 978302109 |
2 | 1 | 914 | 3 | 978301968 |
3 | 1 | 3408 | 4 | 978300275 |
4 | 1 | 2355 | 5 | 978824291 |
... | ... | ... | ... | ... |
1000204 | 6040 | 1091 | 1 | 956716541 |
1000205 | 6040 | 1094 | 5 | 956704887 |
1000206 | 6040 | 562 | 5 | 956704746 |
1000207 | 6040 | 1096 | 4 | 956715648 |
1000208 | 6040 | 1097 | 4 | 956715569 |
#使用pd.merge()函数将ratings、users和movies三个DataFrame合并为一个名为data的新DataFrame。
#这个新的data DataFrame将包含了三个原始DataFrame中的所有列和行,基于它们之间的共同列进行合并。
data = pd.merge(pd.merge(ratings, users), movies)
data#输出data DataFrame合并后的所有数据
data.iloc[0]#输出data DataFrame中的第一行数据
user_id 1 movie_id 1193 rating 5 timestamp 978300760 gender F age 1 occupation 10 zip 48067 title One Flew Over the Cuckoo's Nest (1975) genres Drama Name: 0, dtype: object
#使用pivot_table()函数基于data DataFrame创建了一个名为mean_ratings的新DataFrame,
#该DataFrame计算了每个电影标题(title)在不同性别(gender)下的平均评分。
mean_ratings = data.pivot_table("rating", index="title",columns="gender", aggfunc="mean")
mean_ratings.head(5)#mean_ratings DataFrame 的前5行输出
gender | F | M |
---|---|---|
title | ||
$1,000,000 Duck (1971) | 3.375000 | 2.761905 |
'Night Mother (1986) | 3.388889 | 3.352941 |
'Til There Was You (1997) | 2.675676 | 2.733333 |
'burbs, The (1989) | 2.793478 | 2.962085 |
...And Justice for All (1979) | 3.828571 | 3.689024 |
A:数据透视表是一种数据汇总和聚合的方法,通过对数据进行重新排列和汇总,可以提供对数据的多个维度的聚合结果。数据透视表可以帮助我们更好地理解数据之间的关系和趋势,从而支持更深入的数据分析和决策。数据透视表的主要优势是可以快速而灵活地汇总数据,并以一种直观的方式展示汇总结果。以下是数据透视表的几个关键要素:
1. 行索引:选择一个或多个列作为行索引,根据这些列的值进行数据的分组和分类。行索引决定了透视表中的每一行。
2. 列索引:选择一个或多个列作为列索引,根据这些列的值进行数据的分组和分类。列索引决定了透视表中的每一列。
3. 聚合值:选择一个或多个列作为聚合值,对这些列的值进行聚合计算,如求和、平均值、计数等。聚合值决定了透视表中每个单元格的内容。
4. 聚合函数:选择适当的聚合函数对聚合值进行计算。常见的聚合函数包括求和、平均值、计数、最大值、最小值等。
通过这些要素的组合,可以根据不同的需求和分析目标创建不同形式的数据透视表。数据透视表的结果可以提供对数据的多维度聚合结果,帮助我们发现数据中的模式、趋势和关联性。
#使用groupby()函数对data DataFrame按电影标题(title)进行分组,并计算每个电影标题对应的评分数量。
ratings_by_title = data.groupby("title").size()
ratings_by_title.head()#显示每个电影标题对应的评分数量。
active_titles = ratings_by_title.index[ratings_by_title >= 250]#从评分数量中筛选出了评分数大于等于250的活跃电影标题
active_titles
Index([''burbs, The (1989)', '10 Things I Hate About You (1999)', '101 Dalmatians (1961)', '101 Dalmatians (1996)', '12 Angry Men (1957)', '13th Warrior, The (1999)', '2 Days in the Valley (1996)', '20,000 Leagues Under the Sea (1954)', '2001: A Space Odyssey (1968)', '2010 (1984)', ... 'X-Men (2000)', 'Year of Living Dangerously (1982)', 'Yellow Submarine (1968)', 'You've Got Mail (1998)', 'Young Frankenstein (1974)', 'Young Guns (1988)', 'Young Guns II (1990)', 'Young Sherlock Holmes (1985)', 'Zero Effect (1998)', 'eXistenZ (1999)'], dtype='object', name='title', length=1216)
#使用loc索引器从mean_ratings DataFrame 中选择评分数大于等于250的活跃电影标题
mean_ratings = mean_ratings.loc[active_titles]
mean_ratings
gender | F | M |
---|---|---|
title | ||
'burbs, The (1989) | 2.793478 | 2.962085 |
10 Things I Hate About You (1999) | 3.646552 | 3.311966 |
101 Dalmatians (1961) | 3.791444 | 3.500000 |
101 Dalmatians (1996) | 3.240000 | 2.911215 |
12 Angry Men (1957) | 4.184397 | 4.328421 |
... | ... | ... |
Young Guns (1988) | 3.371795 | 3.425620 |
Young Guns II (1990) | 2.934783 | 2.904025 |
Young Sherlock Holmes (1985) | 3.514706 | 3.363344 |
Zero Effect (1998) | 3.864407 | 3.723140 |
eXistenZ (1999) | 3.098592 | 3.289086 |
#使用rename()函数将mean_ratings DataFrame 中的索引标签从"Seven Samurai (The Magnificent Seven) (Shichinin no samurai) (1954)"更改为
#"Seven Samurai (Shichinin no samurai) (1954)"。
mean_ratings = mean_ratings.rename(index={"Seven Samurai (The Magnificent Seven) (Shichinin no samurai) (1954)":
"Seven Samurai (Shichinin no samurai) (1954)"})
#使用sort_values()函数根据女性观众的平均评分("F"列)对mean_ratings DataFrame 进行降序排序
top_female_ratings = mean_ratings.sort_values("F", ascending=False)
top_female_ratings.head()
gender | F | M | diff |
---|---|---|---|
title | |||
Close Shave, A (1995) | 4.644444 | 4.473795 | -0.170650 |
Wrong Trousers, The (1993) | 4.588235 | 4.478261 | -0.109974 |
Sunset Blvd. (a.k.a. Sunset Boulevard) (1950) | 4.572650 | 4.464589 | -0.108060 |
Wallace & Gromit: The Best of Aardman Animation (1996) | 4.563107 | 4.385075 | -0.178032 |
Schindler's List (1993) | 4.562602 | 4.491415 | -0.071187 |
#通过计算mean_ratings DataFrame 中男性观众平均评分("M"列)与女性观众平均评分("F"列)的差异,将差异值添加为名为"diff"的新列。
mean_ratings["diff"] = mean_ratings["M"] - mean_ratings["F"]
mean_ratings
gender | F | M | diff |
---|---|---|---|
title | |||
'burbs, The (1989) | 2.793478 | 2.962085 | 0.168607 |
10 Things I Hate About You (1999) | 3.646552 | 3.311966 | -0.334586 |
101 Dalmatians (1961) | 3.791444 | 3.500000 | -0.291444 |
101 Dalmatians (1996) | 3.240000 | 2.911215 | -0.328785 |
12 Angry Men (1957) | 4.184397 | 4.328421 | 0.144024 |
... | ... | ... | ... |
Young Guns (1988) | 3.371795 | 3.425620 | 0.053825 |
Young Guns II (1990) | 2.934783 | 2.904025 | -0.030758 |
Young Sherlock Holmes (1985) | 3.514706 | 3.363344 | -0.151362 |
Zero Effect (1998) | 3.864407 | 3.723140 | -0.141266 |
eXistenZ (1999) | 3.098592 | 3.289086 | 0.190494 |
#使用sort_values()函数根据差异值("diff"列)对mean_ratings DataFrame 进行升序排序
sorted_by_diff = mean_ratings.sort_values("diff")
sorted_by_diff.head()
gender | F | M | diff |
---|---|---|---|
title | |||
Dirty Dancing (1987) | 3.790378 | 2.959596 | -0.830782 |
Jumpin' Jack Flash (1986) | 3.254717 | 2.578358 | -0.676359 |
Grease (1978) | 3.975265 | 3.367041 | -0.608224 |
Little Women (1994) | 3.870588 | 3.321739 | -0.548849 |
Steel Magnolias (1989) | 3.901734 | 3.365957 | -0.535777 |
#通过使用切片操作[::-1]对sorted_by_diff DataFrame 进行逆序排序,并返回前5行内容。
sorted_by_diff[::-1].head(5)
gender | F | M | diff |
---|---|---|---|
title | |||
Good, The Bad and The Ugly, The (1966) | 3.494949 | 4.221300 | 0.726351 |
Kentucky Fried Movie, The (1977) | 2.878788 | 3.555147 | 0.676359 |
Dumb & Dumber (1994) | 2.697987 | 3.336595 | 0.638608 |
Longest Day, The (1962) | 3.411765 | 4.031447 | 0.619682 |
Cable Guy, The (1996) | 2.250000 | 2.863787 | 0.613787 |
#使用groupby()函数按电影标题(title)分组,并计算每个电影标题对应的评分标准差。
rating_std_by_title = data.groupby("title")["rating"].std()
#从标准差结果中筛选出评分数大于等于250的活跃电影标题,
rating_std_by_title = rating_std_by_title.loc[active_titles]
rating_std_by_title.head()
title 'burbs, The (1989) 1.107760 10 Things I Hate About You (1999) 0.989815 101 Dalmatians (1961) 0.982103 101 Dalmatians (1996) 1.098717 12 Angry Men (1957) 0.812731 Name: rating, dtype: float64
#使用sort_values()函数将rating_std_by_title Series 对象按评分标准差进行降序排序,并返回前10个最大的标准差值。
rating_std_by_title.sort_values(ascending=False)[:10]
title Dumb & Dumber (1994) 1.321333 Blair Witch Project, The (1999) 1.316368 Natural Born Killers (1994) 1.307198 Tank Girl (1995) 1.277695 Rocky Horror Picture Show, The (1975) 1.260177 Eyes Wide Shut (1999) 1.259624 Evita (1996) 1.253631 Billy Madison (1995) 1.249970 Fear and Loathing in Las Vegas (1998) 1.246408 Bicentennial Man (1999) 1.245533 Name: rating, dtype: float64
#展示"genres"列的前5行数据
movies["genres"].head()
#将每个电影的"genres"列按竖线字符分割成一个列表
movies["genres"].head().str.split("|")
#将分割后的列表赋值给了一个名为"genre"的新列,并删除了原始的"genres"列。这样,"genre"列包含了每个电影的类型列表。
movies["genre"] = movies.pop("genres").str.split("|")
movies.head()
movie_id | title | genre | |
---|---|---|---|
0 | 1 | Toy Story (1995) | [Animation, Children's, Comedy] |
1 | 2 | Jumanji (1995) | [Adventure, Children's, Fantasy] |
2 | 3 | Grumpier Old Men (1995) | [Comedy, Romance] |
3 | 4 | Waiting to Exhale (1995) | [Comedy, Drama] |
4 | 5 | Father of the Bride Part II (1995) | [Comedy] |
#使用explode()函数将"genre"列中的列表元素展开为多行,并将结果存储在名为"movies_exploded"的新DataFrame中。然后显示前10行的数据。
movies_exploded = movies.explode("genre")
movies_exploded[:10]
movie_id | title | genre | |
---|---|---|---|
0 | 1 | Toy Story (1995) | Animation |
0 | 1 | Toy Story (1995) | Children's |
0 | 1 | Toy Story (1995) | Comedy |
1 | 2 | Jumanji (1995) | Adventure |
1 | 2 | Jumanji (1995) | Children's |
1 | 2 | Jumanji (1995) | Fantasy |
2 | 3 | Grumpier Old Men (1995) | Comedy |
2 | 3 | Grumpier Old Men (1995) | Romance |
3 | 4 | Waiting to Exhale (1995) | Comedy |
3 | 4 | Waiting to Exhale (1995) | Drama |
#使用pd.merge()函数将"movies_exploded"、"ratings"和"users"三个DataFrame进行合并,创建一个名为"ratings_with_genre"的新DataFrame。
ratings_with_genre = pd.merge(pd.merge(movies_exploded, ratings), users)
#选择"ratings_with_genre" DataFrame 中的第一行数据。
ratings_with_genre.iloc[0]
#使用groupby()函数对"ratings_with_genre" DataFrame 按"genre"和"age"分组,并计算每个组中"rating"列的平均值。
genre_ratings = (ratings_with_genre.groupby(["genre", "age"])
["rating"].mean()#使用mean()函数计算每个分组的平均值
.unstack("age"))#使用unstack("age")将结果重塑为以"age"作为列索引的形式。
genre_ratings[:10]
age | 1 | 18 | 25 | 35 | 45 | 50 | 56 |
---|---|---|---|---|---|---|---|
genre | |||||||
Action | 3.506385 | 3.447097 | 3.453358 | 3.538107 | 3.528543 | 3.611333 | 3.610709 |
Adventure | 3.449975 | 3.408525 | 3.443163 | 3.515291 | 3.528963 | 3.628163 | 3.649064 |
Animation | 3.476113 | 3.624014 | 3.701228 | 3.740545 | 3.734856 | 3.780020 | 3.756233 |
Children's | 3.241642 | 3.294257 | 3.426873 | 3.518423 | 3.527593 | 3.556555 | 3.621822 |
Comedy | 3.497491 | 3.460417 | 3.490385 | 3.561984 | 3.591789 | 3.646868 | 3.650949 |
Crime | 3.710170 | 3.668054 | 3.680321 | 3.733736 | 3.750661 | 3.810688 | 3.832549 |
Documentary | 3.730769 | 3.865865 | 3.946690 | 3.953747 | 3.966521 | 3.908108 | 3.961538 |
Drama | 3.794735 | 3.721930 | 3.726428 | 3.782512 | 3.784356 | 3.878415 | 3.933465 |
Fantasy | 3.317647 | 3.353778 | 3.452484 | 3.482301 | 3.532468 | 3.581570 | 3.532700 |
Film-Noir | 4.145455 | 3.997368 | 4.058725 | 4.064910 | 4.105376 | 4.175401 | 4.125932 |
美国社会保障局(SSA)提供了从1880年至现在的婴儿姓名频率的数据。可以使用这些数据做很多事情:
根据给定的名字对婴儿名字随时间的比例进行可视化
确定一个名字的相对排位
确定每年最受欢迎的名字,或者流行程度最高或最低的名字
接下来我们将结合代码共同学习这一分析过程。本例题涉及以下知识点,重要处会有解释:
数据分组和聚合:通过使用groupby()
函数对DataFrame进行分组操作,可以根据指定的列对数据进行分组。在代码中,使用groupby()
函数按年份和性别进行分组,并计算每个分组的出生人数总和。
数据排序和排名:通过使用sort_values()
函数对DataFrame进行排序操作,可以根据指定的列对数据进行排序。在代码中,使用sort_values()
函数对数据进行降序排序,以获取出生人数最多的姓名。
数据透视:通过使用pivot_table()
函数,可以对数据进行透视操作,计算每个分组的汇总统计信息。在代码中,使用透视表计算了按年份和性别分组的出生数总和,并将结果存储在total_births
和table
这两个DataFrame中。
数据可视化:使用Matplotlib库的plot()
函数对数据进行可视化。代码中使用折线图和柱状图展示了不同年份和性别的出生人数、比例等信息。通过数据可视化可以更直观地观察数据的趋势和分布情况。
数据处理和筛选:通过对DataFrame应用函数、使用条件语句和基于特定列的筛选,可以对数据进行处理和筛选。在代码中,使用apply()
函数、sort_values()
函数、isin()
函数等对数据进行处理和筛选,以满足特定的需求。
#在读取时,为列指定了列名为["name", "sex", "births"]。
names1880 = pd.read_csv("datasets/babynames/yob1880.txt",
names=["name", "sex", "births"])
names1880
name | sex | births | |
---|---|---|---|
0 | Mary | F | 7065 |
1 | Anna | F | 2604 |
2 | Emma | F | 2003 |
3 | Elizabeth | F | 1939 |
4 | Minnie | F | 1746 |
... | ... | ... | ... |
1995 | Woodie | M | 5 |
1996 | Worthy | M | 5 |
1997 | Wright | M | 5 |
1998 | York | M | 5 |
1999 | Zachariah | M | 5 |
#对 names1880 DataFrame 进行分组,按照性别("sex")进行分组,并计算每个性别对应的出生数("births" 列)的总和。
names1880.groupby("sex")["births"].sum()
sex F 90993 M 110493 Name: births, dtype: int64
A:在数据分析中,经常需要根据某个特定的列对数据进行分组,并对每个分组进行聚合操作,以计算统计指标或得出结论。在本段代码中,我们通过使用groupby()
函数按照年份和性别对数据进行分组,然后使用sum()
函数计算每个分组的出生人数总和。这样可以帮助我们理解和描述数据的整体趋势,比如年份和性别对出生人数的影响。
pieces = []
#代码通过循环遍历从1880年到2010年的文件,并将每个文件的内容读取为DataFrame
for year in range(1880, 2011):
path = f"datasets/babynames/yob{year}.txt"
frame = pd.read_csv(path, names=["name", "sex", "births"])
#为每个DataFrame添加一个名为"year"的列
frame["year"] = year
pieces.append(frame)
#用pd.concat()函数将所有DataFrame连接成一个单独的DataFrame
names = pd.concat(pieces, ignore_index=True)
names#显示该Frame
name | sex | births | year | |
---|---|---|---|---|
0 | Mary | F | 7065 | 1880 |
1 | Anna | F | 2604 | 1880 |
2 | Emma | F | 2003 | 1880 |
3 | Elizabeth | F | 1939 | 1880 |
4 | Minnie | F | 1746 | 1880 |
... | ... | ... | ... | ... |
1690779 | Zymaire | M | 5 | 2010 |
1690780 | Zyonne | M | 5 | 2010 |
1690781 | Zyquarius | M | 5 | 2010 |
1690782 | Zyran | M | 5 | 2010 |
1690783 | Zzyzx | M | 5 | 2010 |
#使用pivot_table()函数计算了按年份和性别分组的出生数总和
total_births = names.pivot_table("births", index="year",
columns="sex", aggfunc=sum)
#显示total_births DataFrame 的最后几行
total_births.tail()
#绘制了一个标题为"Total births by sex and year"的出生数按年份和性别的折线图。
total_births.plot(title="Total births by sex and year")
#定义了一个名为add_prop的函数。该函数接收一个分组,并为该分组计算一个名为"prop"的新列,表示每个名字在该年份和性别分组中的比例,每个名字的出生率
def add_prop(group):
group["prop"] = group["births"] / group["births"].sum()
return group
#将names DataFrame 按年份和性别分组,并对每个分组应用add_prop函数
names = names.groupby(["year", "sex"], group_keys=False).apply(add_prop)
name | sex | births | year | prop | |
---|---|---|---|---|---|
0 | Mary | F | 7065 | 1880 | 0.077643 |
1 | Anna | F | 2604 | 1880 | 0.028618 |
2 | Emma | F | 2003 | 1880 | 0.022013 |
3 | Elizabeth | F | 1939 | 1880 | 0.021309 |
4 | Minnie | F | 1746 | 1880 | 0.019188 |
... | ... | ... | ... | ... | ... |
1690779 | Zymaire | M | 5 | 2010 | 0.000003 |
1690780 | Zyonne | M | 5 | 2010 | 0.000003 |
1690781 | Zyquarius | M | 5 | 2010 | 0.000003 |
1690782 | Zyran | M | 5 | 2010 | 0.000003 |
1690783 | Zzyzx | M | 5 | 2010 | 0.000003 |
#按照 "year" 和 "sex" 进行分组,并计算每个组中的 "prop" 列的总和。
names.groupby(["year", "sex"])["prop"].sum()
year sex 1880 F 1.0 M 1.0 1881 F 1.0 M 1.0 1882 F 1.0 ... 2008 M 1.0 2009 F 1.0 M 1.0 2010 F 1.0 M 1.0 Name: prop, Length: 262, dtype: float64
#该函数接收一个分组,并按照 "births" 列(出生人数)的降序对分组进行排序,然后选择排序结果中的前1000行
def get_top1000(group):
return group.sort_values("births", ascending=False)[:1000]
#按照 "year" 和 "sex" 进行分组,并对每个分组应用 get_top1000 函数
grouped = names.groupby(["year", "sex"])
top1000 = grouped.apply(get_top1000)
top1000.head()
name | sex | births | year | prop | |||
---|---|---|---|---|---|---|---|
year | sex | ||||||
1880 | F | 0 | Mary | F | 7065 | 1880 | 0.077643 |
1 | Anna | F | 2604 | 1880 | 0.028618 | ||
2 | Emma | F | 2003 | 1880 | 0.022013 | ||
3 | Elizabeth | F | 1939 | 1880 | 0.021309 | ||
4 | Minnie | F | 1746 | 1880 | 0.019188 |
#使用reset_index()函数对top1000 DataFrame 进行重置索引,并丢弃原始索引。设置drop=True可以移除原始索引列,以便在重置索引后不保留它。
top1000 = top1000.reset_index(drop=True)
top1000.head()#显示出生人数最多的前5名
name | sex | births | year | prop | |
---|---|---|---|---|---|
0 | Mary | F | 7065 | 1880 | 0.077643 |
1 | Anna | F | 2604 | 1880 | 0.028618 |
2 | Emma | F | 2003 | 1880 | 0.022013 |
3 | Elizabeth | F | 1939 | 1880 | 0.021309 |
4 | Minnie | F | 1746 | 1880 | 0.019188 |
#代码根据 "sex" 列的值将 top1000 DataFrame 分为男孩("M")和女孩("F")两个子数据集。
boys = top1000[top1000["sex"] == "M"]
girls = top1000[top1000["sex"] == "F"]
#使用 pivot_table() 函数对 top1000 DataFrame 进行数据透视,计算了每个名字在不同年份下的出生数总和
total_births = top1000.pivot_table("births", index="year",
columns="name",
aggfunc=sum)
#码使用 info() 方法打印出 total_births DataFrame 的信息,然后从中选择了 "John"、"Harry"、"Mary" 和 "Marilyn" 四个名字的列,
#并绘制了一个子图展示每年的出生数。
total_births.info()
#选择这四个名字进行绘制
subset = total_births[["John", "Harry", "Mary", "Marilyn"]]
#使用 plot() 方法对 subset DataFrame 进行绘图,设置 subplots=True 以绘制子图,figsize=(12, 10) 用于设置图形的大小
subset.plot(subplots=True, figsize=(12, 10),
title="Number of births per year")
import matplotlib.pyplot as plt#创建一个新的画布。
plt.figure()
#使用 pivot_table() 函数计算了 top1000 DataFrame 中 的出生率总和
table = top1000.pivot_table("prop", index="year",
columns="sex", aggfunc=sum)
#使用 plot() 方法绘制了一个折线图,展示了每年男女出生率关系
table.plot(title="Sum of table1000.prop by year and sex",
yticks=np.linspace(0, 1.2, 13))
df = boys[boys["year"] == 2010]#2010年男孩出生人数表
df
name | sex | births | year | prop | |
---|---|---|---|---|---|
260877 | Jacob | M | 21875 | 2010 | 0.011523 |
260878 | Ethan | M | 17866 | 2010 | 0.009411 |
260879 | Michael | M | 17133 | 2010 | 0.009025 |
260880 | Jayden | M | 17030 | 2010 | 0.008971 |
260881 | William | M | 16870 | 2010 | 0.008887 |
... | ... | ... | ... | ... | ... |
261872 | Camilo | M | 194 | 2010 | 0.000102 |
261873 | Destin | M | 194 | 2010 | 0.000102 |
261874 | Jaquan | M | 194 | 2010 | 0.000102 |
261875 | Jaydan | M | 194 | 2010 | 0.000102 |
261876 | Maxton | M | 193 | 2010 | 0.000102 |
#对 DataFrame 中的 "prop" 列进行排序,并计算累计和。然后,展示了累计和的前10个值,
#并使用 searchsorted() 方法找到累计和达到 0.5 时的索引位置。
prop_cumsum = df["prop"].sort_values(ascending=False).cumsum()
prop_cumsum[:10]
prop_cumsum.searchsorted(0.5)
116
#从名为 boys 的 DataFrame 中选择了年份为 1900 的数据,并对该子数据集按 "prop" 列进行降序排序。
#然后,计算累计和,并使用 searchsorted() 方法找到累计和达到 0.5 时的索引位置,最后加上 1。
df = boys[boys.year == 1900]
in1900 = df.sort_values("prop", ascending=False).prop.cumsum()
in1900.searchsorted(0.5) + 1
25
#定义了一个名为 get_quantile_count 的函数。该函数接收一个分组,并按照 "prop" 列的值进行降序排序。然后,计算累计和,
#并使用 searchsorted() 方法找到累计和达到给定分位数 q 时的位置,最后加上 1。
def get_quantile_count(group, q=0.5):
group = group.sort_values("prop", ascending=False)
return group.prop.cumsum().searchsorted(q) + 1
diversity = top1000.groupby(["year", "sex"]).apply(get_quantile_count)
diversity = diversity.unstack()
fig = plt.figure()
diversity.head()
#使用 plot() 方法绘制了一个图表,标题为 "Number of popular names in top 50%"。图表展示了每个年份和性别组合中达到前50%的名字数量。
diversity.plot(title="Number of popular names in top 50%")
#定义了一个名为 get_last_letter 的函数。该函数接收一个字符串 x,并返回字符串的最后一个字母。
def get_last_letter(x):
return x[-1]
#使用 map() 方法将 get_last_letter 函数应用于 names DataFrame 的 "name" 列,将每个名字的最后一个字母提取出来
last_letters = names["name"].map(get_last_letter)
last_letters.name = "last_letter"
#使用 pivot_table() 函数对 names DataFrame 进行数据透视,计算每个年份和性别组合中,根据最后一个字母进行分组的出生数总和
table = names.pivot_table("births", index=last_letters,
columns=["sex", "year"], aggfunc=sum)
#将展示按照特定年份(1910、1960 和 2010)的出生数数据,并且按照性别和最后一个字母的分布。
subtable = table.reindex(columns=[1910, 1960, 2010], level="year")
subtable.head()
sex | F | M | ||||
---|---|---|---|---|---|---|
year | 1910 | 1960 | 2010 | 1910 | 1960 | 2010 |
last_letter | ||||||
a | 108376.0 | 691247.0 | 670605.0 | 977.0 | 5204.0 | 28438.0 |
b | NaN | 694.0 | 450.0 | 411.0 | 3912.0 | 38859.0 |
c | 5.0 | 49.0 | 946.0 | 482.0 | 15476.0 | 23125.0 |
d | 6750.0 | 3729.0 | 2607.0 | 22111.0 | 262112.0 | 44398.0 |
e | 133569.0 | 435013.0 | 313833.0 | 28655.0 | 178823.0 | 129012.0 |
#展示每个年份和性别组合中每个字母的比例,以及它们在对应年份的出生数总和的占比。
subtable.sum()
letter_prop = subtable / subtable.sum()
letter_prop
sex | F | M | ||||
---|---|---|---|---|---|---|
year | 1910 | 1960 | 2010 | 1910 | 1960 | 2010 |
last_letter | ||||||
a | 0.273390 | 0.341853 | 0.381240 | 0.005031 | 0.002440 | 0.014980 |
b | NaN | 0.000343 | 0.000256 | 0.002116 | 0.001834 | 0.020470 |
c | 0.000013 | 0.000024 | 0.000538 | 0.002482 | 0.007257 | 0.012181 |
d | 0.017028 | 0.001844 | 0.001482 | 0.113858 | 0.122908 | 0.023387 |
e | 0.336941 | 0.215133 | 0.178415 | 0.147556 | 0.083853 | 0.067959 |
f | NaN | 0.000010 | 0.000055 | 0.000783 | 0.004325 | 0.001188 |
g | 0.000144 | 0.000157 | 0.000374 | 0.002250 | 0.009488 | 0.001404 |
h | 0.051529 | 0.036224 | 0.075852 | 0.045562 | 0.037907 | 0.051670 |
i | 0.001526 | 0.039965 | 0.031734 | 0.000844 | 0.000603 | 0.022628 |
j | NaN | NaN | 0.000090 | NaN | NaN | 0.000769 |
k | 0.000121 | 0.000156 | 0.000356 | 0.036581 | 0.049384 | 0.018541 |
l | 0.043189 | 0.033867 | 0.026356 | 0.065016 | 0.104904 | 0.070367 |
m | 0.001201 | 0.008613 | 0.002588 | 0.058044 | 0.033827 | 0.024657 |
n | 0.079240 | 0.130687 | 0.140210 | 0.143415 | 0.152522 | 0.362771 |
o | 0.001660 | 0.002439 | 0.001243 | 0.017065 | 0.012829 | 0.042681 |
p | 0.000018 | 0.000023 | 0.000020 | 0.003172 | 0.005675 | 0.001269 |
q | NaN | NaN | 0.000030 | NaN | NaN | 0.000180 |
r | 0.013390 | 0.006764 | 0.018025 | 0.064481 | 0.031034 | 0.087477 |
s | 0.039042 | 0.012764 | 0.013332 | 0.130815 | 0.102730 | 0.065145 |
t | 0.027438 | 0.015201 | 0.007830 | 0.072879 | 0.065655 | 0.022861 |
u | 0.000684 | 0.000574 | 0.000417 | 0.000124 | 0.000057 | 0.001221 |
v | NaN | 0.000060 | 0.000117 | 0.000113 | 0.000037 | 0.001434 |
w | 0.000020 | 0.000031 | 0.001182 | 0.006329 | 0.007711 | 0.016148 |
x | 0.000015 | 0.000037 | 0.000727 | 0.003965 | 0.001851 | 0.008614 |
y | 0.110972 | 0.152569 | 0.116828 | 0.077349 | 0.160987 | 0.058168 |
z | 0.002439 | 0.000659 | 0.000704 | 0.000170 | 0.000184 | 0.001831 |
import matplotlib.pyplot as plt
#使用 Matplotlib 库绘制了一个包含两个子图的图形窗口,每个子图展示了不同性别的字母比例数据
fig, axes = plt.subplots(2, 1, figsize=(10, 8))
#在第一个子图中绘制了一个垂直柱状图。kind="bar" 表示绘制柱状图,rot=0 表示不旋转 x 轴标签,ax=axes[0] 表示绘制在第一个子图上,title="Male" 设置了子图的标题为 "Male",即男性。
letter_prop["M"].plot(kind="bar", rot=0, ax=axes[0], title="Male")
#在第二个子图中绘制了一个垂直柱状图。kind="bar" 表示绘制柱状图,rot=0 表示不旋转 x 轴标签,ax=axes[1] 表示绘制在第二个子图上,
#title="Female" 设置了子图的标题为 "Female",即女性。legend=False 表示不显示图例。
letter_prop["F"].plot(kind="bar", rot=0, ax=axes[1], title="Female",
legend=False)
plt.subplots_adjust(hspace=0.25) # 调整子图之间的垂直间距
letter_prop = table / table.sum() # 计算每个字母在每年出生人数中的比例
dny_ts = letter_prop.loc[["d", "n", "y"], "M"].T # 从letter_prop中选择字母"d", "n", "y"在男性出生人数中的比例,并进行转置操作
dny_ts.head() # 显示dny_ts DataFrame的前几行数据
last_letter | d | n | y |
---|---|---|---|
year | |||
1880 | 0.083055 | 0.153213 | 0.075760 |
1881 | 0.083247 | 0.153214 | 0.077451 |
1882 | 0.085340 | 0.149560 | 0.077537 |
1883 | 0.084066 | 0.151646 | 0.079144 |
1884 | 0.086120 | 0.149915 | 0.080405 |
plt.close("all") # 关闭所有打开的图形窗口
fig = plt.figure() # 创建一个新的Figure对象
dny_ts.plot() # 对dny_ts DataFrame进行折线图的可视化
all_names = pd.Series(top1000["name"].unique()) # 从top1000 DataFrame的"name"列获取唯一的姓名,并将结果存储在all_names变量中
lesley_like = all_names[all_names.str.contains("Lesl")] # 选择all_names中包含"Lesl"的姓名,并将结果赋值给lesley_like变量
lesley_like # 显示lesley_like Series,即包含以"Lesl"开头的姓名
632 Leslie 2294 Lesley 4262 Leslee 4728 Lesli 6103 Lesly dtype: object
filtered = top1000[top1000["name"].isin(lesley_like)] # 根据top1000 DataFrame中的"name"列与lesley_like中的姓名进行匹配,筛选出匹配的行数据,并将结果赋值给filtered变量
filtered.groupby("name")["births"].sum() # 对filtered DataFrame按姓名进行分组,计算每个姓名的出生人数总和,并显示结果
name Leslee 1082 Lesley 35022 Lesli 929 Leslie 370429 Lesly 10067 Name: births, dtype: int64
table = filtered.pivot_table("births", index="year", columns="sex", aggfunc="sum") # 根据年份和性别对filtered DataFrame进行透视,计算每个年份和性别的出生人数总和,并将结果存储在table变量中
table = table.div(table.sum(axis="columns"), axis="index") # 对table DataFrame进行行归一化,即每行的总和作为除数,计算每个年份和性别的归一化比例
table.tail() # 显示table DataFrame的最后几行数据,即归一化后的比例结果
sex | F | M |
---|---|---|
year | ||
2006 | 1.0 | NaN |
2007 | 1.0 | NaN |
2008 | 1.0 | NaN |
2009 | 1.0 | NaN |
2010 | 1.0 | NaN |
fig = plt.figure() # 创建一个新的Figure对象
table.plot(style={"M": "k-", "F": "k--"}) # 对table DataFrame进行折线图的可视化,男性使用黑色实线,女性使用黑色虚线
美国农业部提供了食物营养信息数据库。每种事务都有一些识别属性以及两份营养元素和营养比例的列表。这种形式的数据不适合分析,所以需要做一些工作将数据转换成更好的形式。
接下来我们将结合代码共同学习这一分析过程。本例题涉及以下知识点,重要处会有代码注释:
创建和操作Pandas DataFrame:使用pd.DataFrame()
函数创建Pandas DataFrame对象,使用head()
方法查看DataFrame的前几行数据,使用info()
方法查看DataFrame的基本信息。
数据筛选和计数:使用pd.value_counts()
函数对选定列中的每个唯一值进行计数。
循环和列表操作:使用for
循环遍历列表中的每个元素,使用append()
方法将元素添加到列表中。
合并DataFrame:使用pd.concat()
函数将多个DataFrame对象合并为一个大的DataFrame,使用merge()
函数基于指定的列将两个DataFrame合并。
数据清洗:使用drop_duplicates()
方法删除重复的行。
列重命名:使用rename()
方法重命名DataFrame的列名。
可视化:使用matplotlib.pyplot
库进行可视化,使用plot()
方法绘制条形图。
分组和聚合:使用groupby()
方法对DataFrame进行分组,并使用聚合函数计算每个分组的统计量。
import json
db = json.load(open("datasets/usda_food/database.json"))
# 计算对象db的长度,即列表中元素的数量
len(db)
# 获取db列表中索引为0的元素的所有键
db[0].keys()
# 从db列表中索引为0的元素中获取键为"nutrients"的值的列表,并返回列表中的第一个元素
db[0]["nutrients"][0]
# *注意*将db列表中索引为0的元素中的"nutrients"值转换为Pandas DataFrame对象
nutrients = pd.DataFrame(db[0]["nutrients"])
# 显示nutrients DataFrame的前7行数据
nutrients.head(7)
value | units | description | group | |
---|---|---|---|---|
0 | 25.18 | g | Protein | Composition |
1 | 29.20 | g | Total lipid (fat) | Composition |
2 | 3.06 | g | Carbohydrate, by difference | Composition |
3 | 3.28 | g | Ash | Other |
4 | 376.00 | kcal | Energy | Energy |
5 | 39.28 | g | Water | Composition |
6 | 1573.00 | kJ | Energy | Energy |
A:创建和操作Pandas DataFrame:nutrients = pd.DataFrame(db[0]["nutrients"])将db列表中索引为0的元素中的"nutrients"值转换为Pandas DataFrame对象,并将其存储在nutrients变量中。这样我们可以使用Pandas提供的函数和方法对数据进行处理和分析。nutrients.head(7)使用head()方法显示nutrients DataFrame的前7行数据。
info_keys = ["description", "group", "id", "manufacturer"] # 包含要从数据库中提取的信息的键的列表
info = pd.DataFrame(db, columns=info_keys) # 使用info_keys作为列名,创建包含db数据的Pandas DataFrame对象,并将其存储在info变量中
info.head() # 显示info DataFrame的前几行数据
info.info() # 显示info DataFrame的基本信息,包括列名、每列的非空值数量、每列的数据类型等
RangeIndex: 6636 entries, 0 to 6635 Data columns (total 4 columns): # Column Non-Null Count Dtype --- ------ -------------- ----- 0 description 6636 non-null object 1 group 6636 non-null object 2 id 6636 non-null int64 3 manufacturer 5195 non-null object dtypes: int64(1), object(3) memory usage: 207.5+ KB
pd.value_counts(info["group"])[:10]
'''
info["group"]:从DataFrame info 中选择了名为 "group" 的列,该列包含了食物的分组信息。
pd.value_counts():对选定列中的每个唯一值进行计数,并返回计数结果。
[:10]:取计数结果中的前 10 个值,即返回出现次数最多的前 10 个分组
'''
Vegetables and Vegetable Products 812 Beef Products 618 Baked Products 496 Breakfast Cereals 403 Legumes and Legume Products 365 Fast Foods 365 Lamb, Veal, and Game Products 345 Sweets 341 Fruits and Fruit Juices 328 Pork Products 328 Name: group, dtype: int64
A:info_keys = ["description", "group", "id", "manufacturer"]定义了一个包含要从数据库中提取的信息的键的列表。
info = pd.DataFrame(db, columns=info_keys)使用info_keys作为列名,创建包含db数据的Pandas DataFrame对象,并将其存储在info变量中。
info.head()显示info DataFrame的前几行数据。info.info()显示info DataFrame的基本信息,包括列名、每列的非空值数量、每列的数据类型等。
pd.value_counts(info["group"])[:10]对info["group"]列中的每个唯一值进行计数,并返回计数结果的前10个值,即返回出现次数最多的前10个分组。
nutrients = [] # 创建一个空列表用于存储营养信息
for rec in db:
fnuts = pd.DataFrame(rec["nutrients"]) # 为每个记录创建一个包含营养信息的DataFrame对象
fnuts["id"] = rec["id"] # 添加一个名为"id"的列,将记录的id值赋给该列的每个元素
nutrients.append(fnuts) # 将每个记录的营养信息DataFrame添加到nutrients列表中
nutrients = pd.concat(nutrients, ignore_index=True) # 将nutrients列表中的DataFrame对象合并为一个大的DataFrame,并重新索引行号
nutrients
value | units | description | group | id | |
---|---|---|---|---|---|
0 | 25.180 | g | Protein | Composition | 1008 |
1 | 29.200 | g | Total lipid (fat) | Composition | 1008 |
2 | 3.060 | g | Carbohydrate, by difference | Composition | 1008 |
3 | 3.280 | g | Ash | Other | 1008 |
4 | 376.000 | kcal | Energy | Energy | 1008 |
... | ... | ... | ... | ... | ... |
389350 | 0.000 | mcg | Vitamin B-12, added | Vitamins | 43546 |
389351 | 0.000 | mg | Cholesterol | Other | 43546 |
389352 | 0.072 | g | Fatty acids, total saturated | Other | 43546 |
389353 | 0.028 | g | Fatty acids, total monounsaturated | Other | 43546 |
389354 | 0.041 | g | Fatty acids, total polyunsaturated | Other | 43546 |
nutrients.duplicated().sum() # 统计重复行的数量
nutrients = nutrients.drop_duplicates() # 删除重复的行
col_mapping = {"description" : "food",
"group" : "fgroup"}
info = info.rename(columns=col_mapping, copy=False) # 重命名info DataFrame的列名为food和fgroup
info.info() # 显示info DataFrame的基本信息
col_mapping = {"description" : "nutrient",
"group" : "nutgroup"}
nutrients = nutrients.rename(columns=col_mapping, copy=False) # 重命名nutrients DataFrame的列名为nutrient和nutgroup
nutrients # 显示重命名后的nutrients DataFrame
RangeIndex: 6636 entries, 0 to 6635 Data columns (total 4 columns): # Column Non-Null Count Dtype --- ------ -------------- ----- 0 food 6636 non-null object 1 fgroup 6636 non-null object 2 id 6636 non-null int64 3 manufacturer 5195 non-null object dtypes: int64(1), object(3) memory usage: 207.5+ KB
value | units | nutrient | nutgroup | id | |
---|---|---|---|---|---|
0 | 25.180 | g | Protein | Composition | 1008 |
1 | 29.200 | g | Total lipid (fat) | Composition | 1008 |
2 | 3.060 | g | Carbohydrate, by difference | Composition | 1008 |
3 | 3.280 | g | Ash | Other | 1008 |
4 | 376.000 | kcal | Energy | Energy | 1008 |
... | ... | ... | ... | ... | ... |
389350 | 0.000 | mcg | Vitamin B-12, added | Vitamins | 43546 |
389351 | 0.000 | mg | Cholesterol | Other | 43546 |
389352 | 0.072 | g | Fatty acids, total saturated | Other | 43546 |
389353 | 0.028 | g | Fatty acids, total monounsaturated | Other | 43546 |
389354 | 0.041 | g | Fatty acids, total polyunsaturated | Other | 43546 |
ndata = pd.merge(nutrients, info, on="id") # 基于"id"列将nutrients和info两个DataFrame进行合并,并将结果存储在ndata中
ndata.info() # 显示ndata DataFrame的基本信息
ndata.iloc[30000] # 获取ndata DataFrame中索引为30000的行的数据
Int64Index: 375176 entries, 0 to 375175 Data columns (total 8 columns): # Column Non-Null Count Dtype --- ------ -------------- ----- 0 value 375176 non-null float64 1 units 375176 non-null object 2 nutrient 375176 non-null object 3 nutgroup 375176 non-null object 4 id 375176 non-null int64 5 food 375176 non-null object 6 fgroup 375176 non-null object 7 manufacturer 293054 non-null object dtypes: float64(1), int64(1), object(6) memory usage: 25.8+ MB value 0.04 units g nutrient Glycine nutgroup Amino Acids id 6158 food Soup, tomato bisque, canned, condensed fgroup Soups, Sauces, and Gravies manufacturer Name: 30000, dtype: object
fig = plt.figure() # 创建一个新的空白Figure对象,并将其赋值给变量fig
result = ndata.groupby(["nutrient", "fgroup"])["value"].quantile(0.5) # 根据"nutrient"和"fgroup"列进行分组,计算"value"列的中位数,并将结果存储在result变量中
result["Zinc, Zn"].sort_values().plot(kind="barh") # 从result中选择"Zinc, Zn"列的数据,对数据进行排序,然后使用水平条形图进行可视化
A:在Python中,有多个库可以用于数据可视化,下面是一些基本的可视化操作示例:
1.折线图:使用matplotlib
库可以创建折线图来显示数据的趋势和变化:
import matplotlib.pyplot as plt
# 创建数据
x = [1, 2, 3, 4, 5]
y = [10, 15, 7, 12, 9]
# 绘制折线图
plt.plot(x, y)
# 添加标签和标题
plt.xlabel('X轴')
plt.ylabel('Y轴')
plt.title('折线图')
# 显示图形
plt.show()
2.柱状图:柱状图常用于显示不同类别之间的比较:
import matplotlib.pyplot as plt
# 创建数据
categories = ['A', 'B', 'C', 'D']
values = [10, 15, 7, 12]
# 绘制柱状图
plt.bar(categories, values)
# 添加标签和标题
plt.xlabel('类别')
plt.ylabel('数值')
plt.title('柱状图')
# 显示图形
plt.show()
3.散点图:散点图用于显示两个变量之间的关系:
import matplotlib.pyplot as plt
# 创建数据
x = [1, 2, 3, 4, 5]
y = [10, 15, 7, 12, 9]
# 绘制散点图
plt.scatter(x, y)
# 添加标签和标题
plt.xlabel('X轴')
plt.ylabel('Y轴')
plt.title('散点图')
# 显示图形
plt.show()
4.直方图:直方图用于显示数据的分布情况:
import matplotlib.pyplot as plt
# 创建数据
data = [1, 2, 2, 3, 3, 3, 4, 4, 5]
# 绘制直方图
plt.hist(data)
# 添加标签和标题
plt.xlabel('数值')
plt.ylabel('频数')
plt.title('直方图')
# 显示图形
plt.show()
回到本例题:
by_nutrient = ndata.groupby(["nutgroup", "nutrient"]) # 根据"nutgroup"和"nutrient"列进行分组,将结果存储在by_nutrient变量中
def get_maximum(x):
return x.loc[x.value.idxmax()] # 定义一个函数get_maximum,用于获取每个分组中"value"列取得最大值的行
max_foods = by_nutrient.apply(get_maximum)[["value", "food"]] # 对每个分组应用get_maximum函数,获取"value"列最大值所对应的行,并选择"value"和"food"两列
max_foods["food"] = max_foods["food"].str[:50] # 将"food"列的字符串长度截取为最多50个字符
max_foods.loc["Amino Acids"]["food"] # 从 max_foods DataFrame 中选择索引为 "Amino Acids" 的行,返回一个包含该行数据的 Series。从该 Series 中选择名为 "food" 的列,返回 "Amino Acids" 分类下食物名称的数据。
nutrient Alanine Gelatins, dry powder, unsweetened Arginine Seeds, sesame flour, low-fat Aspartic acid Soy protein isolate Cystine Seeds, cottonseed flour, low fat (glandless) Glutamic acid Soy protein isolate Glycine Gelatins, dry powder, unsweetened Histidine Whale, beluga, meat, dried (Alaska Native) Hydroxyproline KENTUCKY FRIED CHICKEN, Fried Chicken, ORIGINA... Isoleucine Soy protein isolate, PROTEIN TECHNOLOGIES INTE... Leucine Soy protein isolate, PROTEIN TECHNOLOGIES INTE... Lysine Seal, bearded (Oogruk), meat, dried (Alaska Na... Methionine Fish, cod, Atlantic, dried and salted Phenylalanine Soy protein isolate, PROTEIN TECHNOLOGIES INTE... Proline Gelatins, dry powder, unsweetened Serine Soy protein isolate, PROTEIN TECHNOLOGIES INTE... Threonine Soy protein isolate, PROTEIN TECHNOLOGIES INTE... Tryptophan Sea lion, Steller, meat with fat (Alaska Native) Tyrosine Soy protein isolate, PROTEIN TECHNOLOGIES INTE... Valine Soy protein isolate, PROTEIN TECHNOLOGIES INTE... Name: food, dtype: object
美国联邦选举委员会公布了有关政治运动贡献的数据。这些数据包括捐赠者姓名、职业和雇主、地址和缴费金额。你可以尝试做以下分析:
1.按职业和雇主的捐赠统计;2.按捐赠金额统计;3.按州进行统计。
接下来我们将结合代码共同学习这一分析过程。本例题涉及以下知识点,重要处会有解释:
获取唯一值:fec["cand_nm"].unique()
从fec
DataFrame的"cand_nm"列获取唯一的候选人姓名,并将结果存储在unique_cands
变量中。
数据分组:grouped = fec.groupby("cand_nm")
根据候选人姓名对fec
DataFrame进行分组。
数据透视表:by_occupation = fec.pivot_table("contb_receipt_amt", index="contbr_occupation", columns="party", aggfunc="sum")
使用pivot_table()
函数创建数据透视表,对"contb_receipt_amt"列进行求和聚合,以"contbr_occupation"为行索引,以"party"为列索引。
可视化:over_2mm.plot(kind="barh")
对DataFrame进行可视化,例如水平条形图。
数据映射:使用字典对列中的值进行映射,例如fec["contbr_occupation"].map(get_occ)
使用自定义函数get_occ
将"contbr_occupation"列中的值进行映射。
数据堆叠和展开:grouped.size().unstack(level=0)
计算每个候选人在不同区间标签下的数据量,并将结果转换为宽格式,以候选人姓名为列。
数据透视表和缺失值填充:totals = grouped["contb_receipt_amt"].sum().unstack(level=0).fillna(0)
计算每个候选人在不同州的总捐款金额,将结果转换为宽格式,并用0填充缺失值。
数据归一化和比例计算:percent = totals.div(totals.sum(axis="columns"), axis="index")
对每个州的总捐款金额进行归一化,以每行的总和作为除数,计算归一化后的比例。
fec = pd.read_csv("datasets/fec/P00000001-ALL.csv", low_memory=False) # 从CSV文件中读取数据,并将结果存储在fec变量中,low_memory参数设置为False以确保读取所有数据
fec.info() # 显示fec DataFrame的基本信息
fec.iloc[123456] # 获取fec DataFrame中索引为123456的行的数据
RangeIndex: 1001731 entries, 0 to 1001730 Data columns (total 16 columns): # Column Non-Null Count Dtype --- ------ -------------- ----- 0 cmte_id 1001731 non-null object 1 cand_id 1001731 non-null object 2 cand_nm 1001731 non-null object 3 contbr_nm 1001731 non-null object 4 contbr_city 1001712 non-null object 5 contbr_st 1001727 non-null object 6 contbr_zip 1001620 non-null object 7 contbr_employer 988002 non-null object 8 contbr_occupation 993301 non-null object 9 contb_receipt_amt 1001731 non-null float64 10 contb_receipt_dt 1001731 non-null object 11 receipt_desc 14166 non-null object 12 memo_cd 92482 non-null object 13 memo_text 97770 non-null object 14 form_tp 1001731 non-null object 15 file_num 1001731 non-null int64 dtypes: float64(1), int64(1), object(14) memory usage: 122.3+ MB cmte_id C00431445 cand_id P80003338 cand_nm Obama, Barack contbr_nm ELLMAN, IRA contbr_city TEMPE contbr_st AZ contbr_zip 852816719 contbr_employer ARIZONA STATE UNIVERSITY contbr_occupation PROFESSOR contb_receipt_amt 50.0 contb_receipt_dt 01-DEC-11 receipt_desc NaN memo_cd NaN memo_text NaN form_tp SA17A file_num 772372 Name: 123456, dtype: object
unique_cands = fec["cand_nm"].unique() # 从fec DataFrame的"cand_nm"列获取唯一的候选人姓名,将结果存储在unique_cands变量中
unique_cands # 显示unique_cands数组,即唯一的候选人姓名列表
unique_cands[2] # 获取unique_cands数组中索引为2的元素,即第三个候选人的姓名
parties = {"Bachmann, Michelle": "Republican",
"Cain, Herman": "Republican",
"Gingrich, Newt": "Republican",
"Huntsman, Jon": "Republican",
"Johnson, Gary Earl": "Republican",
"McCotter, Thaddeus G": "Republican",
"Obama, Barack": "Democrat",
"Paul, Ron": "Republican",
"Pawlenty, Timothy": "Republican",
"Perry, Rick": "Republican",
"Roemer, Charles E. 'Buddy' III": "Republican",
"Romney, Mitt": "Republican",
"Santorum, Rick": "Republican"}
# 设置党派
fec["cand_nm"][123456:123461] # 获取fec DataFrame中"cand_nm"列索引为123456到123460的行的数据
fec["cand_nm"][123456:123461].map(parties) # 对索引为123456到123460的"cand_nm"列的数据应用parties字典,将候选人姓名映射为对应的党派
fec["party"] = fec["cand_nm"].map(parties) # 将通过parties字典映射后的党派信息添加为fec DataFrame的一个新列,列名为"party"
fec["party"].value_counts() # 统计"party"列中每个党派的数量,并返回计数结果
Democrat 593746 Republican 407985 Name: party, dtype: int64
(fec["contb_receipt_amt"] > 0).value_counts() # 统计"contb_receipt_amt"列中大于0的值的数量和小于等于0的值的数量
True 991475 False 10256 Name: contb_receipt_amt, dtype: int64
fec = fec[fec["contb_receipt_amt"] > 0] # 从fec DataFrame中筛选出"contb_receipt_amt"列大于0的行,并将结果重新赋值给fec变量
fec_mrbo = fec[fec["cand_nm"].isin(["Obama, Barack", "Romney, Mitt"])] # 从fec DataFrame中筛选出"cand_nm"列包含"Obama, Barack"或"Romney, Mitt"的行,并将结果赋值给fec_mrbo变量
fec["contbr_occupation"].value_counts()[:10] # 统计"contbr_occupation"列中各个职业的数量,并返回前10个结果
RETIRED 233990 INFORMATION REQUESTED 35107 ATTORNEY 34286 HOMEMAKER 29931 PHYSICIAN 23432 INFORMATION REQUESTED PER BEST EFFORTS 21138 ENGINEER 14334 TEACHER 13990 CONSULTANT 13273 PROFESSOR 12555 Name: contbr_occupation, dtype: int64
occ_mapping = {
"INFORMATION REQUESTED PER BEST EFFORTS" : "NOT PROVIDED",
"INFORMATION REQUESTED" : "NOT PROVIDED",
"INFORMATION REQUESTED (BEST EFFORTS)" : "NOT PROVIDED",
"C.E.O.": "CEO"
} # 映射 “前” 为 “后”
def get_occ(x):
# 如果映射未提供,则返回 x
return occ_mapping.get(x, x)
fec["contbr_occupation"] = fec["contbr_occupation"].map(get_occ) # 使用 get_occ 函数将 "contbr_occupation" 列中的值进行映射
emp_mapping = {
"INFORMATION REQUESTED PER BEST EFFORTS" : "NOT PROVIDED",
"INFORMATION REQUESTED" : "NOT PROVIDED",
"SELF" : "SELF-EMPLOYED",
"SELF EMPLOYED" : "SELF-EMPLOYED",
} # 映射 “前” 为 “后”
def get_emp(x):
# 如果映射未提供,则返回 x
return emp_mapping.get(x, x)
fec["contbr_employer"] = fec["contbr_employer"].map(get_emp) # 使用 get_emp 函数将 "contbr_employer" 列中的值进行映射
by_occupation = fec.pivot_table("contb_receipt_amt", # 使用"contb_receipt_amt"列作为值进行透视
index="contbr_occupation", # 使用"contbr_occupation"列作为行索引
columns="party", # 使用"party"列作为列索引
aggfunc="sum") # 对值进行求和聚合
over_2mm = by_occupation[by_occupation.sum(axis="columns") > 2000000] # 选择总和大于2000000的行并赋值给over_2mm变量
over_2mm # 显示over_2mm DataFrame
party | Democrat | Republican |
---|---|---|
contbr_occupation | ||
ATTORNEY | 11141982.97 | 7477194.43 |
CEO | 2074974.79 | 4211040.52 |
CONSULTANT | 2459912.71 | 2544725.45 |
ENGINEER | 951525.55 | 1818373.70 |
EXECUTIVE | 1355161.05 | 4138850.09 |
HOMEMAKER | 4248875.80 | 13634275.78 |
INVESTOR | 884133.00 | 2431768.92 |
LAWYER | 3160478.87 | 391224.32 |
MANAGER | 762883.22 | 1444532.37 |
NOT PROVIDED | 4866973.96 | 20565473.01 |
OWNER | 1001567.36 | 2408286.92 |
PHYSICIAN | 3735124.94 | 3594320.24 |
PRESIDENT | 1878509.95 | 4720923.76 |
PROFESSOR | 2165071.08 | 296702.73 |
REAL ESTATE | 528902.09 | 1625902.25 |
RETIRED | 25305116.38 | 23561244.49 |
SELF-EMPLOYED | 672393.40 | 1640252.54 |
plt.figure() # 创建一个新的空白Figure对象
over_2mm.plot(kind="barh") # 对over_2mm DataFrame进行水平条形图的可视化
def get_top_amounts(group, key, n=5):
totals = group.groupby(key)["contb_receipt_amt"].sum() # 对group中的数据按key进行分组,计算每个组中"contb_receipt_amt"列的总和
return totals.nlargest(n) # 返回总和最大的前n个组
grouped = fec_mrbo.groupby("cand_nm") # 根据候选人姓名对fec_mrbo DataFrame进行分组
grouped.apply(get_top_amounts, "contbr_occupation", n=7) # 对分组后的数据应用get_top_amounts函数,按"contbr_occupation"列获取每个组的前7个最大总和
grouped.apply(get_top_amounts, "contbr_employer", n=10) # 对分组后的数据应用get_top_amounts函数,按"contbr_employer"列获取每个组的前10个最大总和
cand_nm contbr_employer Obama, Barack RETIRED 22694358.85 SELF-EMPLOYED 17080985.96 NOT EMPLOYED 8586308.70 INFORMATION REQUESTED 5053480.37 HOMEMAKER 2605408.54 SELF 1076531.20 SELF EMPLOYED 469290.00 STUDENT 318831.45 VOLUNTEER 257104.00 MICROSOFT 215585.36 Romney, Mitt INFORMATION REQUESTED PER BEST EFFORTS 12059527.24 RETIRED 11506225.71 HOMEMAKER 8147196.22 SELF-EMPLOYED 7409860.98 STUDENT 496490.94 CREDIT SUISSE 281150.00 MORGAN STANLEY 267266.00 GOLDMAN SACH & CO. 238250.00 BARCLAYS CAPITAL 162750.00 H.I.G. CAPITAL 139500.00 Name: contb_receipt_amt, dtype: float64
bins = np.array([0, 1, 10, 100, 1000, 10000, 100_000, 1_000_000, 10_000_000]) # 定义一个包含分箱边界值的NumPy数组,用于将"contb_receipt_amt"列的值分成不同的区间
labels = pd.cut(fec_mrbo["contb_receipt_amt"], bins) # 使用pd.cut函数根据指定的分箱边界将"contb_receipt_amt"列的值进行分箱,并返回一个包含对应区间标签的Series
labels # 显示labels Series,即包含了每个"contb_receipt_amt"值所属的区间标签
411 (10, 100] 412 (100, 1000] 413 (100, 1000] 414 (10, 100] 415 (10, 100] ... 701381 (10, 100] 701382 (100, 1000] 701383 (1, 10] 701384 (10, 100] 701385 (100, 1000] Name: contb_receipt_amt, Length: 694282, dtype: category Categories (8, interval[int64, right]): [(0, 1] < (1, 10] < (10, 100] < (100, 1000] < (1000, 10000] < (10000, 100000] < (100000, 1000000] < (1000000, 10000000]]
grouped = fec_mrbo.groupby(["cand_nm", labels]) # 根据候选人姓名和labels对fec_mrbo DataFrame进行分组
grouped.size().unstack(level=0) # 计算每个候选人在不同区间标签下的数据量,并将结果转换为宽格式(以候选人姓名为列),并显示该结果
cand_nm | Obama, Barack | Romney, Mitt |
---|---|---|
contb_receipt_amt | ||
(0, 1] | 493 | 77 |
(1, 10] | 40070 | 3681 |
(10, 100] | 372280 | 31853 |
(100, 1000] | 153991 | 43357 |
(1000, 10000] | 22284 | 26186 |
(10000, 100000] | 2 | 1 |
(100000, 1000000] | 3 | 0 |
(1000000, 10000000] | 4 |
plt.figure() # 创建一个新的空白Figure对象
bucket_sums = grouped["contb_receipt_amt"].sum().unstack(level=0) # 计算每个候选人在不同区间标签下的"contb_receipt_amt"列的总和,并转换为宽格式(以候选人姓名为列)
normed_sums = bucket_sums.div(bucket_sums.sum(axis="columns"), axis="index") # 对每个候选人在不同区间标签下的总和进行归一化,以每行的总和作为除数,计算归一化后的比例
normed_sums # 显示归一化后的比例结果
normed_sums[:-2].plot(kind="barh") # 对前面除去最后两行的归一化比例结果进行水平条形图的可视化
grouped = fec_mrbo.groupby(["cand_nm", "contbr_st"]) # 根据候选人姓名和捐赠者所在州(contbr_st)对fec_mrbo DataFrame进行分组
totals = grouped["contb_receipt_amt"].sum().unstack(level=0).fillna(0) # 计算每个候选人在不同州的总捐款金额,将结果转换为宽格式(以候选人姓名为列),并用0填充缺失值
totals = totals[totals.sum(axis="columns") > 100000] # 筛选出总捐款金额超过100000的行
totals.head(10) # 显示前10行的totals DataFrame
cand_nm | Obama, Barack | Romney, Mitt |
---|---|---|
contbr_st | ||
AK | 281840.15 | 86204.24 |
AL | 543123.48 | 527303.51 |
AR | 359247.28 | 105556.00 |
AZ | 1506476.98 | 1888436.23 |
CA | 23824984.24 | 11237636.60 |
CO | 2132429.49 | 1506714.12 |
CT | 2068291.26 | 3499475.45 |
DC | 4373538.80 | 1025137.50 |
DE | 336669.14 | 82712.00 |
FL | 7318178.58 | 8338458.81 |
percent = totals.div(totals.sum(axis="columns"), axis="index") # 对每个州的总捐款金额进行归一化,以每行的总和作为除数,计算归一化后的比例
percent.head(10) # 显示前10行的percent DataFrame,即每个州的归一化比例结果
cand_nm | Obama, Barack | Romney, Mitt |
---|---|---|
contbr_st | ||
AK | 0.765778 | 0.234222 |
AL | 0.507390 | 0.492610 |
AR | 0.772902 | 0.227098 |
AZ | 0.443745 | 0.556255 |
CA | 0.679498 | 0.320502 |
CO | 0.585970 | 0.414030 |
CT | 0.371476 | 0.628524 |
DC | 0.810113 | 0.189887 |
DE | 0.802776 | 0.197224 |
FL | 0.467417 | 0.532583 |
A:数据归一化(Data normalization)是一种常用的数据预处理技术,用于将不同特征或变量的取值范围进行统一,使其具有相同的尺度或比例。数据归一化的目的是消除不同特征之间的量纲差异,以便更好地进行数据分析和建模。
在上述代码中,我们对每个州的总捐款金额进行了归一化和比例计算:
首先对每行的数据进行求和,计算每个州的总捐款金额。使用`div()`函数将每个州的总捐款金额除以每行的总和。这样做的目的是将每个州的总捐款金额归一化,并计算出每个州的捐款金额占总捐款金额的比例。
`totals.sum(axis="columns")`表示沿着行方向进行求和。
`axis="index"`表示将每行的总和作为除数。
最后显示归一化和比例计算后的结果,即每个州的归一化比例结果。