【面向初学者】四个例子带你了解如何《利用Python进行数据分析》

前言

我们提供了四个数据集供大家学习,分别是:

1.MoviesLens 1M数据集

 2.美国1880-2010年的婴儿名字​  

3.美国农业部视频数据库  

4.2012年联邦选举委员会数据库

大家可以根据自己的兴趣进行选择阅读,关于数据以及jupyter  notebook笔记包,大家可以点击这里下载链接:点击这里
 


1.MoviesLens 1M数据集

GroupLens实验室提供了一些从MoviesLens用户那里收集的20世纪90年代末到21世纪初的电影评分数据的集合。这些数据提供了电影的评分、流派、年份和观众数据(年龄、邮编、性别、职业)。 MovisLens1M数据集包含6000个用户对4000部电影的100万个评分。数据分布在三个表格之中:分别包含评分、用户信息和电影信息。

首先我们使用 pandas 读取 "movielens" 数据集的三个文件:users.dat、ratings.dat 和 movies.dat

import pandas as pd

unames = ["user_id", "gender", "age", "occupation", "zip"]
users = pd.read_csv("datasets/movielens/users.dat", sep="::",
                    header=None, names=unames, engine="python")

rnames = ["user_id", "movie_id", "rating", "timestamp"]
ratings = pd.read_csv("datasets/movielens/ratings.dat", sep="::",
                      header=None, names=rnames, engine="python")

mnames = ["movie_id", "title", "genres"]
movies = pd.read_csv("datasets/movielens/movies.dat", sep="::",
                     header=None, names=mnames, engine="python")

 首先我们了解一下head函数,head() 函数对于快速查看数据框的开头几行非常有用,特别是在处理大型数据集时,可以帮助您了解数据的结构和格式。通过查看数据集的前几行,您可以获得关于数据列的名称、数据值的类型以及可能的缺失值等信息

# 打印 users 数据集的前5行
print(users.head(5))

# 打印 ratings 数据集的前5行
print(ratings.head(5))

# 打印 movies 数据集的前5行
print(movies.head(5))

接下来为了方便数据的处理,我们使用 pd.merge() 函数将 ratingsusers 数据集按照共同的列(例如 user_id)进行合并,然后再将合并后的结果与 movies 数据集按照共同的列(例如 movie_id)进行合并。

接下来,通过 data 变量打印了合并后的数据集。这将显示合并后的数据集中的所有行和列。

如果想要访问合并后数据集的第一行,可以使用 iloc 属性,通过索引 0 来获取第一行的数据

#使用pd.merge()函数将ratings、users和movies三个DataFrame合并
data = pd.merge(pd.merge(ratings, users), movies)

#输出data DataFrame合并后的所有数据
data

#输出data DataFrame中的第一行数据
data.iloc[0]

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 进行数据透视,以计算不同电影标题(title)在不同性别(gender)下的平均评分。

mean_ratings = data.pivot_table("rating", index="title",
                                columns="gender", aggfunc="mean")
mean_ratings.head(5)

这段代码将返回一个新的数据集 mean_ratings,其中每一行表示一个电影标题(title),每一列表示一个性别(gender),对应的值为该电影在该性别下的平均评分。

通过调用 head(5) 方法,代码展示了 mean_ratings 数据集的前 5 行,

genderFM
title
$1,000,000 Duck (1971)3.3750002.761905
'Night Mother (1986)3.3888893.352941
'Til There Was You (1997)2.6756762.733333
'burbs, The (1989)2.7934782.962085
...And Justice for All (1979)3.8285713.689024

使用 groupby() 函数将数据集 data 按照电影标题(title)进行分组,然后使用 size() 方法计算每个电影标题对应的评分数量。

# 根据电影标题对评分数据进行分组,并计算每个电影标题的评分数量
ratings_by_title = data.groupby("title").size()
ratings_by_title.head()

# 输出ratings_by_title数据集的前5行,显示每个电影标题对应的评分数量

title
$1,000,000 Duck (1971)            37
'Night Mother (1986)              70
'Til There Was You (1997)         52
'burbs, The (1989)               303
...And Justice for All (1979)    199
dtype: int64
# 筛选出评分数量大于等于250的活跃电影标题
active_titles = ratings_by_title.index[ratings_by_title >= 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)

我们使用mean_ratings.loc[active_titles]mean_ratings 数据集中选择出活跃电影标题对应的行。创建新的数据集

#返回一个新的数据集 mean_ratings,其中只包含活跃电影标题对应的行和相应的列(不同性别的平均评分)
mean_ratings = mean_ratings.loc[active_titles]

#打印数据集
mean_ratings

genderFM
title
'burbs, The (1989)2.7934782.962085
10 Things I Hate About You (1999)3.6465523.311966
101 Dalmatians (1961)3.7914443.500000
101 Dalmatians (1996)3.2400002.911215
12 Angry Men (1957)4.1843974.328421
.........
Young Guns (1988)3.3717953.425620
Young Guns II (1990)2.9347832.904025
Young Sherlock Holmes (1985)3.5147063.363344
Zero Effect (1998)3.8644073.723140
eXistenZ (1999)3.0985923.289086

mean_ratings.rename() 用于重命名 mean_ratings 数据集的索引

#使用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)"})

通过执行这段代码,可以更新 mean_ratings 数据集中的特定电影标题索引,以便更准确地匹配其他数据集或进行更清晰的分析。


使用mean_ratings.sort_values("F", ascending=False)mean_ratings 数据集按照 "F" 列(女性评分)进行降序排序。

这段代码会返回一个新的数据集 top_female_ratings,其中包含了按照女性评分降序排列的电影标题,并显示前几行结果。

#使用sort_values()函数根据女性观众的平均评分("F"列)对mean_ratings DataFrame 进行降序排序
top_female_ratings = mean_ratings.sort_values("F", ascending=False)
top_female_ratings.head()
genderFM
title
Close Shave, A (1995)4.6444444.473795
Wrong Trousers, The (1993)4.5882354.478261
Sunset Blvd. (a.k.a. Sunset Boulevard) (1950)4.5726504.464589
Wallace & Gromit: The Best of Aardman Animation (1996)4.5631074.385075
Schindler's List (1993)4.5626024.491415

执行这段代码,您可以获取在女性观众中评分最高的电影,并查看它们的平均评分。


mean_ratings 数据集中创建一个名为 "diff" 的新列。该列包含了每部电影的男性评分与女性评分之间的差异。

#创建一个新列diff,表示男女评分的差值
mean_ratings["diff"] = mean_ratings["M"] - mean_ratings["F"]

#打印数据集
mean_ratings

通过执行这段代码,您可以计算出每部电影男性评分和女性评分之间的差异,并将其保存在名为 "diff" 的新列中。

genderFMdiff
title
$1,000,000 Duck (1971)3.3750002.761905-0.613095
'Night Mother (1986)3.3888893.352941-0.035948
'Til There Was You (1997)2.6756762.7333330.057658
'burbs, The (1989)2.7934782.9620850.168607
...And Justice for All (1979)3.8285713.689024-0.139547
............
Zed & Two Noughts, A (1985)3.5000003.380952-0.119048
Zero Effect (1998)3.8644073.723140-0.141266
Zero Kelvin (Kjærlighetens kjøtere) (1995)NaN3.500000NaN
Zeus and Roxanne (1997)2.7777782.357143-0.420635
eXistenZ (1999)3.0985923.2890860.190494

按照升序对数据集排列

#使用sort_values()函数根据差异值("diff"列)对mean_ratings DataFrame 进行升序排序
sorted_by_diff = mean_ratings.sort_values("diff")
sorted_by_diff.head()

genderFMdiff
title
Dirty Dancing (1987)3.7903782.959596-0.830782
Jumpin' Jack Flash (1986)3.2547172.578358-0.676359
Grease (1978)3.9752653.367041-0.608224
Little Women (1994)3.8705883.321739-0.548849
Steel Magnolias (1989)3.9017343.365957-0.535777

#通过使用切片操作[::-1]对sorted_by_diff DataFrame 进行逆序排序,并返回前5行内容。
sorted_by_diff[::-1].head(5)
genderFMdiff
title
Good, The Bad and The Ugly, The (1966)3.4949494.2213000.726351
Kentucky Fried Movie, The (1977)2.8787883.5551470.676359
Dumb & Dumber (1994)2.6979873.3365950.638608
Longest Day, The (1962)3.4117654.0314470.619682
Cable Guy, The (1996)2.2500002.8637870.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_idtitlegenre
01Toy Story (1995)[Animation, Children's, Comedy]
12Jumanji (1995)[Adventure, Children's, Fantasy]
23Grumpier Old Men (1995)[Comedy, Romance]
34Waiting to Exhale (1995)[Comedy, Drama]
45Father of the Bride Part II (1995)[Comedy]

movies_exploded = movies.explode("genre")
movies_exploded[:10]
movie_idtitlegenre
01Toy Story (1995)Animation
01Toy Story (1995)Children's
01Toy Story (1995)Comedy
12Jumanji (1995)Adventure
12Jumanji (1995)Children's
12Jumanji (1995)Fantasy
23Grumpier Old Men (1995)Comedy
23Grumpier Old Men (1995)Romance
34Waiting to Exhale (1995)Comedy
34Waiting to Exhale (1995)Drama


使用了两次 pd.merge() 方法将 movies_explodedratingsusers 数据框进行合并,得到名为 ratings_with_genre 的新数据框。这个新的数据框包含了电影的详细信息、评分信息和用户信息。

接下来,使用 .iloc[0] 选择了 ratings_with_genre 数据框中的第一行数据,并进行了打印显示

#使用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]
movie_id                     1
title         Toy Story (1995)
genre                Animation
user_id                      1
rating                       5
timestamp            978824268
gender                       F
age                          1
occupation                  10
zip                      48067
Name: 0, dtype: object

通过使用 .groupby() 方法按照 "genre" 和 "age" 列进行分组,计算了每个电影类型在不同年龄段的平均评分,并使用 .unstack("age") 方法对结果进行了重塑,得到一个包含电影类型为索引、年龄段为列的新数据框 genre_ratings

最后,使用切片操作 [:10] 显示 genre_ratings 数据框的前10行内容,其中包含了每个电影类型在不同年龄段的平均评分。


#使用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]
age1182535455056
genre
Action3.5063853.4470973.4533583.5381073.5285433.6113333.610709
Adventure3.4499753.4085253.4431633.5152913.5289633.6281633.649064
Animation3.4761133.6240143.7012283.7405453.7348563.7800203.756233
Children's3.2416423.2942573.4268733.5184233.5275933.5565553.621822
Comedy3.4974913.4604173.4903853.5619843.5917893.6468683.650949
Crime3.7101703.6680543.6803213.7337363.7506613.8106883.832549
Documentary3.7307693.8658653.9466903.9537473.9665213.9081083.961538
Drama3.7947353.7219303.7264283.7825123.7843563.8784153.933465
Fantasy3.3176473.3537783.4524843.4823013.5324683.5815703.532700
Film-Noir4.1454553.9973684.0587254.0649104.1053764.1754014.125932

MoviesLens 1M数据集的案例就介绍到这里了


2.美国1880-2010年的婴儿名字

美国社会保障局(SSA)提供了从1880年至现在的婴儿姓名频率的数据。可以使用这些数据做很多事情:
根据给定的名字对婴儿名字随时间的比例进行可视化
确定一个名字的相对排位
确定每年最受欢迎的名字,或者流行程度最高或最低的名字

下面进入代码实例


首先使用 pd.read_csv() 方法读取了名为 "yob1880.txt" 的文件,并将其存储为名为 names1880 的 pandas 数据框。

在读取文件时,指定列名,将第一列命名为 "name",第二列命名为 "sex",第三列命名为 "births"。

这样,您就创建了一个包含 "name"、"sex" 和 "births" 列的数据框 names1880,其中每一行代表了一个在1880年出生的婴儿的信息。

#在读取时,为列指定了列名为["name", "sex", "births"]。
names1880 = pd.read_csv("datasets/babynames/yob1880.txt",
                        names=["name", "sex", "births"])
names1880
namesexbirths
0MaryF7065
1AnnaF2604
2EmmaF2003
3ElizabethF1939
4MinnieF1746
............
1995WoodieM5
1996WorthyM5
1997WrightM5
1998YorkM

5

1999ZachariahM5

names1880 数据框按照性别进行分组,并计算每个性别的出生人数总和 

#对 names1880 DataFrame 进行分组,按照性别("sex")进行分组,并计算每个性别对应的出生数("births" 列)的总和。
names1880.groupby("sex")["births"].sum()
sex
F     90993
M    110493
Name: births, dtype: int64


接下来,我们创建一个名为 names 的 pandas 数据框,该数据框将包含从1880年到2010年期间的婴儿姓名数据。

在代码中,使用一个循环来遍历不同年份的文件,并将每个文件读取为一个独立的数据框。然后,将每个数据框中的 "name"、"sex" 和 "births" 列保留,并添加了一个名为 "year" 的列,用于存储对应的年份。最后,使用 pd.concat() 方法将所有的数据框连接成一个整体的数据框 names

通过将 ignore_index=True 参数传递给 pd.concat() 方法,可以重新为连接后的数据框生成连续的行索引。

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

下面是打印的结果

namesexbirthsyear
0MaryF70651880
1AnnaF26041880
2EmmaF20031880
3ElizabethF19391880
4MinnieF17461880
...............
1690779ZymaireM52010
1690780ZyonneM52010
1690781ZyquariusM52010
1690782ZyranM52010
1690783ZzyzxM52010

我们尝试用折线图更加直观的展现不同年份和性别分组的出生数总和

使用了 pivot_table() 函数对 names 数据框进行操作,计算了按年份和性别分组的出生数总和

通过指定 "births" 作为要聚合的值列,在 index="year"columns="sex" 的基础上进行分组,使用 sum 函数进行求和计算。这样,就创建了一个名为 total_births 的数据框,其中行索引为年份,列索引为性别,值为对应的出生数总和。

使用 tail() 方法显示了 total_births 数据框的最后几行,以便查看最近几年的数据。 

最后使用 plot() 方法绘制了一个折线图,标题为 "Total births by sex and year",展示了按年份和性别分组的出生数总和的趋势。

#使用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" 的新列,表示每个名字在该年份和性别分组中的比例(即出生率)。

add_prop 函数中,通过将每个名字对应的出生数除以该年份和性别分组中所有名字的出生数总和,计算了每个名字的出生率。然后,将计算结果存储在一个名为 "prop" 的新列中,并将修改后的分组返回。

接下来,使用 groupby() 方法按照 "year" 和 "sex" 进行分组,并通过 apply() 方法将 add_prop 函数应用于每个分组。这样,更新了 names 数据框,并在每个分组中添加了一个名为 "prop" 的新列,表示每个名字的出生率。

#定义了一个名为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)


使用 groupby() 方法按照 "year" 和 "sex" 对 names 数据框进行分组,并选择了 "prop" 列。然后,使用 sum() 方法计算了每个年份和性别组合中的出生率之和。

#按照 "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

下面定义一个名为 get_top1000 的函数。该函数接收一个分组,并按照 "births" 列降序排序,然后返回前1000个结果。

接下来,使用 groupby() 方法按照 "year" 和 "sex" 对 names 数据框进行分组,并将结果存储在 grouped 变量中。

然后,使用 apply() 方法将 get_top1000 函数应用于每个分组,用于获取每个年份和性别组合中的前1000个出生人数最高的名字。

最后,通过调用 head() 方法显示了 top1000 数据框的前几行,以查看结果。

#该函数接收一个分组,并按照 "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()


使用 reset_index() 函数对 top1000 数据框进行重置索引,并丢弃了原始索引。通过设置 drop=True 参数,您移除了原始索引列,确保在重置索引后不保留它。

然后,调用 head() 方法显示了经过重置索引后的 top1000 数据框的前几行,以查看出生人数最多的前5名。

#使用reset_index()函数对top1000 DataFrame 进行重置索引,并丢弃原始索引。设置drop=True可以移除原始索引列,以便在重置索引后不保留它。
top1000 = top1000.reset_index(drop=True)
top1000.head()#显示出生人数最多的前5名


使用条件语句将 top1000 数据框按照 "sex" 列的值分为两个子数据集。其中,boys 数据集包含性别为男孩("M")的记录,girls 数据集包含性别为女孩("F")的记录。

接下来,您使用 pivot_table() 函数对 top1000 数据框进行数据透视,以计算每个名字在不同年份下的出生数总和。在透视表中,"births" 列作为值,"year" 列作为索引,"name" 列作为列,并使用 sum 函数进行聚合计算。

这样,您得到了一个新的名为 total_births 的数据框,它显示了每个名字在不同年份下的出生数总和。

#代码根据 "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 数据框的详细信息。这将显示数据框的概要,包括列名、每列的非空值数量以及每列的数据类型等。

然后,创建了一个名为 subset 的新数据框,其中只包含 "John"、"Harry"、"Mary" 和 "Marilyn" 这四个名字的列。通过选择这些列,您可以更加关注和分析这些特定的名字在不同年份下的出生数总和。

接下来,调用 plot() 方法来对 subset 数据框进行绘图。设置 subplots=True 参数会将每个名字的出生数总和作为独立的子图进行绘制。通过设置 figsize=(12, 10) 参数,您可以控制绘图的大小。最后,您设置了图表的标题为 "Number of births per year"。

这样,将得到一个包含四个子图的图表,每个子图显示了每个名字随年份变化的出生数总和。

#码使用 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")

 


使用 pivot_table() 函数计算了 top1000 数据框中每年男性和女性的出生率总和。"prop" 列被用作值,"year" 列被用作索引,"sex" 列被用作列。聚合函数 sum 被用于计算每年的出生率总和。

使用 plot() 方法绘制了一个折线图,展示了每年男性和女性的出生率关系。图表的标题设置为 "Sum of table1000.prop by year and sex"。yticks 参数被设置为 np.linspace(0, 1.2, 13),以控制 y 轴刻度的范围和间隔。

这样,将得到一个显示每年男性和女性出生率总和的折线图。

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))


从名为 boys 的数据框中选择了 "year" 列等于 2010 的行,并将结果保存在名为 df 的新数据框中。

通过这个操作,得到了一个名为 df 的数据框,其中包含了 2010 年男孩的出生人数表。这个数据框包含了以下列:年份 ("year")、姓名 ("name")、性别 ("sex")、出生人数 ("births") 和出生率 ("prop") 等信息,下面进一步查看 df 数据框

df = boys[boys["year"] == 2010]#2010年男孩出生人数表
df

首先对 df 数据框中的 "prop" 列进行了排序,并计算了累计和。这通过使用 sort_values(ascending=False) 方法对 "prop" 列进行降序排序,然后使用 cumsum() 方法计算累计和,并将结果保存在名为 prop_cumsum 的变量中。

接下来,调用了 prop_cumsum[:10] 来展示累计和的前10个值。这将显示 prop_cumsum 变量的前10个元素,它们表示排序后的 "prop" 列累计和的前10个值。

最后,使用 searchsorted(0.5) 方法来找到累计和达到 0.5 时的索引位置。searchsorted() 方法返回第一个大于等于指定值的元素的索引,在这里即是累计和超过或等于 0.5 时的索引位置。

#对 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 的数据,并将结果保存在名为 df 的新数据框中。

接下来,对 df 数据框按 "prop" 列进行降序排序,使用 sort_values("prop", ascending=False) 方法实现。排序后,您计算了累计和,通过使用 cumsum() 方法对 "prop" 列进行累计求和,并将结果保存在名为 in1900 的变量中。

最后,使用 in1900.searchsorted(0.5) + 1 找到累计和达到 0.5 时的索引位置,并在结果上加上 1。searchsorted() 方法返回第一个大于等于指定值的元素的索引,加上 1 即为累计和超过或等于 0.5 时的位置索引。

#从名为 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" 列的值进行降序排序。

在函数内部,首先对传入的 group 分组数据框按照 "prop" 列进行降序排序,使用 sort_values("prop", ascending=False) 方法实现。排序后,计算累计和,通过使用 cumsum() 方法对 "prop" 列进行累计求和,并将结果保存在 group.prop.cumsum() 中。

接下来,使用 searchsorted(q) 方法找到累计和达到给定分位数 q 时的位置索引。searchsorted() 方法返回第一个大于等于指定值的元素的索引,这里即为累计和超过或等于给定分位数 q 时的位置索引。最后,将结果上加 1,以得到符合直觉的位置索引。

接下来,使用 top1000.groupby(["year", "sex"]).apply(get_quantile_count)top1000 数据框按年份和性别进行分组,并对每个分组应用 get_quantile_count 函数。最终,将结果保存在名为 diversity 的新数据框中。

最后,使用 diversity.unstack()diversity 数据框进行重塑,将性别作为列索引,年份作为行索引,以重新排列数据框的结构。

#定义了一个名为 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 的图表对象。

接下来,调用 diversity.head() 方法显示了 diversity 数据框的前几行数据。

然后,使用 diversity.plot(title="Number of popular names in top 50%") 方法绘制图表。该图表的标题为 "Number of popular names in top 50%",并展示了每个年份和性别组合中达到前50%的名字数量的变化情况。

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,并返回该字符串的最后一个字母。函数内部通过使用索引 -1 来获取字符串的最后一个字符。

接下来,使用 map() 方法将 get_last_letter 函数应用于 names 数据框的 "name" 列,目的是将每个名字的最后一个字母提取出来。提取的结果保存在一个新的 Series 对象中,命名为 last_letters

然后,通过对 last_letters Series 对象设置 name 属性为 "last_letter",将其命名为 "last_letter"。

最后,使用 pivot_table() 函数对 names 数据框进行数据透视操作。将 "last_letters" 作为行索引,将 ["sex", "year"] 作为列索引,计算根据最后一个字母进行分组的出生数总和。计算结果保存在名为 table 的新数据框中。

#定义了一个名为 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()

sexFM
year191019602010191019602010
last_letter
a108376.0691247.0670605.0977.05204.028438.0
bNaN694.0450.0411.03912.038859.0
c5.049.0946.0482.015476.023125.0
d6750.03729.02607.022111.0262112.044398.0
e133569.0435013.0313833.028655.0178823.0129012.0

#展示每个年份和性别组合中每个字母的比例,以及它们在对应年份的出生数总和的占比。
subtable.sum()
letter_prop = subtable / subtable.sum()
letter_prop

sexFM
year191019602010191019602010
last_letter
a0.2733900.3418530.3812400.0050310.0024400.014980
bNaN0.0003430.0002560.0021160.0018340.020470
c0.0000130.0000240.0005380.0024820.0072570.012181
d0.0170280.0018440.0014820.1138580.1229080.023387
e0.3369410.2151330.1784150.1475560.0838530.067959
fNaN0.0000100.0000550.0007830.0043250.001188
g0.0001440.0001570.0003740.0022500.0094880.001404
h0.0515290.0362240.0758520.0455620.0379070.051670
i0.0015260.0399650.0317340.0008440.0006030.022628
jNaNNaN0.000090NaNNaN0.000769
k0.0001210.0001560.0003560.0365810.0493840.018541
l0.0431890.0338670.0263560.0650160.1049040.070367
m0.0012010.0086130.0025880.0580440.0338270.024657
n0.0792400.1306870.1402100.1434150.1525220.362771
o0.0016600.0024390.0012430.0170650.0128290.042681
p0.0000180.0000230.0000200.0031720.0056750.001269
qNaNNaN0.000030NaNNaN0.000180
r0.0133900.0067640.0180250.0644810.0310340.087477
s0.0390420.0127640.0133320.1308150.1027300.065145
t0.0274380.0152010.0078300.0728790.0656550.022861
u0.0006840.0005740.0004170.0001240.0000570.001221
vNaN0.0000600.0001170.0001130.0000370.001434
w0.0000200.0000310.0011820.0063290.0077110.016148
x0.0000150.0000370.0007270.0039650.0018510.008614
y0.1109720.1525690.1168280.0773490.1609870.058168
z0.0024390.0006590.0007040.0001700.0001840.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_letterdny
year
18800.0830550.1532130.075760
18810.0832470.1532140.077451
18820.0853400.1495600.077537
18830.0840660.1516460.079144
18840.0861200.1499150.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的最后几行数据,即归一化后的比例结果
sexFM
year
20061.0NaN
20071.0NaN
20081.0NaN
20091.0NaN
20101.0NaN

fig = plt.figure()  # 创建一个新的Figure对象
 
table.plot(style={"M": "k-", "F": "k--"})  # 对table DataFrame进行折线图的可视化,男性使用黑色实线,女性使用黑色虚线

3.美国农业部视频数据库

美国农业部提供了食物营养信息数据库。每种事务都有一些识别属性以及两份营养元素和营养比例的列表。这种形式的数据不适合分析,所以需要做一些工作将数据转换成更好的形式。

以下为代码实例


首先使用 open() 函数打开名为 "datasets/usda_food/database.json" 的 JSON 文件,并将其加载到 db 变量中。这里使用 json.load() 方法来读取 JSON 数据,并通过 open() 函数打开对应的文件。

接下来,使用 len(db) 来获取 db 数据库中的条目数量。len() 函数用于返回给定对象的长度或项目的数量。

import json
db = json.load(open("datasets/usda_food/database.json"))
 
# 计算对象db的长度,即列表中元素的数量
len(db)
6636

db 是一个包含JSON数据的列表。

db[0].keys() 返回列表中第一个字典元素的所有键。

db[0]["nutrients"][0] 获取了第一个字典元素中 "nutrients" 键对应的值的第一个元素。

接下来,根据第一个字典元素中 "nutrients" 键对应的值创建了一个名为 nutrients 的 pandas DataFrame 对象。

最后,使用 head(7) 方法显示了 nutrients DataFrame 的前7行数据。

# 获取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)

将索引为0的元素中的"nutrients"值转换为Pandas DataFrame对象的目的是为了方便对该数据进行进一步的分析和处理。

数据库中的"nutrients"字段可能包含多个营养信息,每个营养信息都具有不同的属性(如名称、单位、值等)。将其转换为DataFrame对象后,可以更方便地应用Pandas库提供的各种功能和方法来处理和分析这些数据。

DataFrame是Pandas库中最常用的数据结构之一,它可以提供灵活的行列操作、数据筛选、聚合计算、可视化等功能。通过将"nutrients"字段转换为DataFrame,可以轻松地利用Pandas提供的各种函数和方法来探索和分析这些营养信息数据。


首先定义了一个名为 info_keys 的列表,其中包含要从数据库中提取的信息的键。

接下来,使用 pd.DataFrame() 函数将 db 数据库中的数据以及 info_keys 列表作为参数,创建了一个名为 info 的 Pandas DataFrame 对象。columns=info_keys 参数用于指定列名,确保新创建的 DataFrame 对象的列与 info_keys 中的键对应。

然后,使用 head() 方法显示了 info DataFrame 的前几行数据,默认显示前5行数据。

最后,使用 info() 方法显示了 info DataFrame 的基本信息,包括列名、每列的非空值数量、每列的数据类型等。

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的基本信息,包括列名、每列的非空值数量、每列的数据类型等
<class 'pandas.core.frame.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


info["group"]info DataFrame 中 "group" 列的数据。使用 pd.value_counts() 函数对该列进行计数,并且使用切片操作符指定只显示前10个计数结果。

pd.value_counts(info["group"]) 返回一个包含不同组别及其对应计数的 Series 对象。

[:10] 则是用于切片操作,表示只显示前10行计数结果。

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


首先创建了一个空列表 nutrients 用于存储营养信息。

然后,使用 for 循环遍历 db 中的每个记录。对于每个记录,使用 pd.DataFrame() 创建一个名为 fnuts 的 DataFrame 对象,其中包含该记录的营养信息。

接下来,使用 fnuts["id"] = rec["id"] 将一个名为 "id" 的列添加到 fnuts DataFrame 中,并将该记录的 id 值赋给该列的每个元素。这样可以将每个记录的 id 与营养信息关联起来。

然后,使用 nutrients.append(fnuts) 将每个记录的营养信息 DataFrame 添加到 nutrients 列表中。

最后,使用 pd.concat(nutrients, ignore_index=True)nutrients 列表中的所有 DataFrame 对象合并为一个大的 DataFrame,并重新索引行号。将合并后的结果重新赋值给 nutrients 变量。

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

valueunitsdescriptiongroupid
025.180gProteinComposition1008
129.200gTotal lipid (fat)Composition1008
23.060gCarbohydrate, by differenceComposition1008
33.280gAshOther1008
4376.000kcalEnergyEnergy1008
..................
3893500.000mcgVitamin B-12, addedVitamins43546
3893510.000mgCholesterolOther43546
3893520.072gFatty acids, total saturatedOther43546
3893530.028gFatty acids, total monounsaturatedOther43546
3893540.041gFatty acids, total polyunsaturatedOther43546

首先,使用 nutrients.duplicated().sum() 统计营养信息 DataFrame 中重复行的数量。

然后,使用 nutrients = nutrients.drop_duplicates() 删除营养信息 DataFrame 中的重复行。

接下来,使用字典 col_mapping 定义了两个列名映射关系,将 "description" 列重命名为 "food",将 "group" 列重命名为 "fgroup"。

使用 info = info.rename(columns=col_mapping, copy=False) 将 info DataFrame 的列名按照映射关系进行重命名,其中 copy=False 表示直接在原地修改。

使用 info.info() 显示 info DataFrame 的基本信息,包括列名、每列的非空值数量、每列的数据类型等。

接着,使用字典 col_mapping 定义了另外两个列名映射关系,将 "description" 列重命名为 "nutrient",将 "group" 列重命名为 "nutgroup"。

使用 nutrients = nutrients.rename(columns=col_mapping, copy=False) 将 nutrients DataFrame 的列名按照映射关系进行重命名,其中 copy=False 表示直接在原地修改。

最后,通过执行 nutrients 显示重命名后的 nutrients DataFrame,即显示具有重命名列的营养信息 DataFrame。

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
 

首先,使用 pd.merge(nutrients, info, on="id") 基于 "id" 列将 nutrients 和 info 两个 DataFrame 进行合并,并将结果存储在 ndata 中。

然后,使用 ndata.info() 显示 ndata DataFrame 的基本信息,包括列名、每列的非空值数量、每列的数据类型等。

接着,使用 ndata.iloc[30000] 获取 ndata DataFrame 中索引为 30000 的行的数据,并将其显示出来。这将显示索引为 30000 的行的所有列的值。

ndata = pd.merge(nutrients, info, on="id")  # 基于"id"列将nutrients和info两个DataFrame进行合并,并将结果存储在ndata中
 
ndata.info()  # 显示ndata DataFrame的基本信息
 
ndata.iloc[30000]  # 获取ndata DataFrame中索引为30000的行的数据
 
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。

然后,使用 ndata.groupby(["nutrient", "fgroup"])["value"].quantile(0.5) 根据 "nutrient" 和 "fgroup" 列进行分组,计算 "value" 列的中位数,并将结果存储在 result 变量中。这将得到一个以 "nutrient" 和 "fgroup" 为索引的 Pandas Series 对象。

接下来,使用 result["Zinc, Zn"].sort_values().plot(kind="barh") 从 result 中选择 "Zinc, Zn" 列的数据,对数据进行排序,然后使用水平条形图进行可视化。这将显示出以 "Zinc, Zn" 为横轴、中位数值为纵轴的条形图。

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"列的数据,对数据进行排序,然后使用水平条形图进行可视化

 


首先,使用 by_nutrient = ndata.groupby(["nutgroup", "nutrient"]) 根据 "nutgroup" 和 "nutrient" 列进行分组,将结果存储在 by_nutrient 变量中。这将得到一个按照 "nutgroup" 和 "nutrient" 进行分组的 GroupBy 对象。

接下来,定义了一个函数 get_maximum(x),用于获取每个分组中 "value" 列取得最大值的行。函数内部使用了 x.loc[x.value.idxmax()],通过找到对应最大值的索引,从原始数据中选择相应的行。

然后,使用 by_nutrient.apply(get_maximum)[["value", "food"]] 对每个分组应用 get_maximum 函数,获取 "value" 列最大值所对应的行,并选择 "value" 和 "food" 两列。这将得到一个包含每个分类下取得最大值的食物名称和对应的最大值的 DataFrame。

接着,使用 max_foods["food"] = max_foods["food"].str[:50] 将 "food" 列的字符串长度截取为最多 50 个字符。这将修改 "food" 列中的字符串,使其最多包含 50 个字符。

最后,使用 max_foods.loc["Amino Acids"]["food"] 从 max_foods DataFrame 中选择索引为 "Amino Acids" 的行,返回一个包含该行数据的 Series。然后从该 Series 中选择名为 "food" 的列,返回 "Amino Acids" 分类下食物名称的数据。

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

4.2012年联邦选举委员会数据库

美国联邦选举委员会公布了有关政治运动贡献的数据。这些数据包括捐赠者姓名、职业和雇主、地址和缴费金额。你可以尝试做以下分析:
1.按职业和雇主的捐赠统计;2.按捐赠金额统计;3.按州进行统计。

以下是代码实例


 fec.info() 是一个用于显示 DataFrame 基本信息的方法。它将显示有关 DataFrame 的列数、每列的名称、非空值的数量以及每列的数据类型等信息

fec = pd.read_csv("datasets/fec/P00000001-ALL.csv", low_memory=False)  # 从CSV文件中读取数据,并将结果存储在fec变量中,low_memory参数设置为False以确保读取所有数据
 
fec.info()  # 显示fec DataFrame的基本信息
 
<class 'pandas.core.frame.DataFrame'>
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

fec.iloc[] 返回指定行和列位置索引上的数据。如果只提供 row_index 参数,则返回整行的数据;如果同时提供 row_indexcolumn_index 参数,则返回指定行和列交叉位置上的数据。

尝试想获取某一行的值

fec.iloc[123456]


unique_cands = fec["cand_nm"].unique() 这一行代码获取了名为 fec 的 DataFrame 中 "cand_nm" 列中唯一的候选人姓名,并将结果存储在名为 unique_cands 的变量中。

unique_cands 是一个数组,包含了唯一的候选人姓名列表。

unique_cands[2] 表示获取 unique_cands 数组中索引为 2 的元素,即第三个候选人的姓名。请注意,索引是从 0 开始的,因此 unique_cands[2] 表示获取第三个候选人的姓名。

unique_cands = fec["cand_nm"].unique()  # 从fec DataFrame的"cand_nm"列获取唯一的候选人姓名,将结果存储在unique_cands变量中
unique_cands  # 显示unique_cands数组,即唯一的候选人姓名列表
unique_cands[2]  # 获取unique_cands数组中索引为2的元素,即第三个候选人的姓名
'Obama, Barack'

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) 返回一个布尔值 Series,其中对于大于 0 的值为 True,小于等于 0 的值为 False。

.value_counts() 对该布尔值 Series 进行统计,返回每个唯一值的数量 

(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 变量。这一行代码用于过滤掉贡献金额小于等于0的行。

fec_mrbo = fec[fec["cand_nm"].isin(["Obama, Barack", "Romney, Mitt"])]:从 fec DataFrame 中筛选出 "cand_nm" 列包含 "Obama, Barack" 或 "Romney, Mitt" 的行,并将结果赋值给 fec_mrbo 变量。这一行代码用于创建一个新的 DataFrame,其中只包含 Obama 和 Romney 的相关数据。

fec["contbr_occupation"].value_counts()[:10]:统计 "contbr_occupation" 列中各个职业的数量,并返回前 10 个结果。这一行代码会返回出现频次最高的前 10 个职业及其对应的数量。

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 是一个字典,用于将 "contbr_occupation" 列中的某些职业名称进行映射。其中,键是需要映射的原始职业名称,值是映射后的职业名称。

get_occ(x) 是一个函数,用于接受一个输入 x(即职业名称),并根据 occ_mapping 字典进行映射。如果 x 在 occ_mapping 中有对应的映射值,则返回映射后的职业名称,否则返回 x 本身。

fec["contbr_occupation"].map(get_occ) 将 "contbr_occupation" 列中的值应用 get_occ 函数进行映射。这一行代码将会对 "contbr_occupation" 列中的每个职业名称进行映射处理,并将结果赋值回 "contbr_occupation" 列。

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 是一个字典,用于将 "contbr_employer" 列中的某些雇主名称进行映射。其中,键是需要映射的原始雇主名称,值是映射后的雇主名称。

get_emp(x) 是一个函数,用于接受一个输入 x(即雇主名称),并根据 emp_mapping 字典进行映射。如果 xemp_mapping 中有对应的映射值,则返回映射后的雇主名称,否则返回 x 本身。 

fec["contbr_employer"].map(get_emp) 将 "contbr_employer" 列中的值应用 get_emp 函数进行映射。这一行代码将会对 "contbr_employer" 列中的每个雇主名称进行映射处理,并将结果赋值回 "contbr_employer" 列。

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" 列中的值进行映射

fec.pivot_table() 是一个透视表函数,用于根据指定的行、列和聚合函数对数据进行透视。在这段代码中,使用 "contb_receipt_amt" 列作为值进行透视,使用 "contbr_occupation" 列作为行索引,使用 "party" 列作为列索引,使用 "sum" 聚合函数对值进行求和。

by_occupation 是透视后的结果,它是一个包含职业和党派作为行列索引的数据表,以 "contb_receipt_amt" 作为值。

over_2mm = by_occupation[by_occupation.sum(axis="columns") > 2000000] 选择了总和大于2000000的行,并将结果赋值给变量 over_2mm。这行代码使用了布尔索引,通过计算每行的总和并判断是否大于2000000来选择行。

over_2mm 是一个 DataFrame,其中包含了总和大于2000000的职业和党派的数据。

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
partyDemocratRepublican
contbr_occupation
ATTORNEY11141982.977477194.43
CEO2074974.794211040.52
CONSULTANT2459912.712544725.45
ENGINEER951525.551818373.70
EXECUTIVE1355161.054138850.09
HOMEMAKER4248875.8013634275.78
INVESTOR884133.002431768.92
LAWYER3160478.87391224.32
MANAGER762883.221444532.37
NOT PROVIDED4866973.9620565473.01
OWNER1001567.362408286.92
PHYSICIAN3735124.943594320.24
PRESIDENT1878509.954720923.76
PROFESSOR2165071.08296702.73
REAL ESTATE528902.091625902.25
RETIRED25305116.3823561244.49
SELF-EMPLOYED672393.401640252.54


 #对over_2mm DataFrame进行水平条形图的可视化 

plt.figure()  # 创建一个新的空白Figure对象
 
over_2mm.plot(kind="barh")  # 对over_2mm DataFrame进行水平条形图的可视化
 


get_top_amounts() 是一个自定义函数,接受三个参数 groupkeyn。在这个函数中,首先对 group 中的数据按照 key 进行分组,然后计算每个分组中 "contb_receipt_amt" 列的总和。最后返回总和最大的前 n 个组。

grouped = fec_mrbo.groupby("cand_nm") 对 DataFrame fec_mrbo 根据 "cand_nm"(候选人姓名)进行分组,并将分组结果赋值给 grouped 变量。

grouped.apply(get_top_amounts, "contbr_occupation", n=7) 对分组后的数据应用自定义函数 get_top_amounts,其中 key 为 "contbr_occupation"(捐助者职业),限制返回每个组中前 7 个总和最大的组。

grouped.apply(get_top_amounts, "contbr_employer", n=10) 对分组后的数据应用自定义函数 get_top_amounts,其中 key 为 "contbr_employer"(捐助者雇主),限制返回每个组中前 10 个总和最大的组。

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

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 是一个包含了每个 "contb_receipt_amt" 值所属的区间标签的 Series。

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]) 根据 "cand_nm"(候选人姓名)和 labels 对 DataFrame fec_mrbo 进行分组。这意味着数据将按照候选人姓名和区间标签进行分组。

grouped.size().unstack(level=0) 首先使用 size() 方法计算每个分组的大小,即每个候选人在不同区间标签下的数据量。然后使用 unstack() 方法将结果转换为宽格式,即以候选人姓名为列。最后,显示转换后的结果。

grouped = fec_mrbo.groupby(["cand_nm", labels])  # 根据候选人姓名和labels对fec_mrbo DataFrame进行分组
 
grouped.size().unstack(level=0)  # 计算每个候选人在不同区间标签下的数据量,并将结果转换为宽格式(以候选人姓名为列),并显示该结果
cand_nmObama, BarackRomney, Mitt

contb_receipt_amt

(0, 1]49377
(1, 10]400703681
(10, 100]37228031853
(100, 1000]15399143357
(1000, 10000]2228426186
(10000, 100000]21
(100000, 1000000]30

(1000000, 10000000]

40

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") 对每个候选人在不同区间标签下的总和进行归一化,即计算每个候选人在每个区间的比例。使用 div() 方法,将每行的总和作为除数,进行归一化计算。

normed_sums 是归一化后的比例结果。

normed_sums[:-2].plot(kind="barh") 对前面除去最后两行的归一化比例结果进行水平条形图的可视化。使用 plot() 方法,设置 kind="barh" 表示绘制水平条形图。

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"]) 根据 "cand_nm"(候选人姓名)和 "contbr_st"(捐赠者所在州)对 DataFrame fec_mrbo 进行分组。这意味着数据将按照候选人姓名和捐赠者所在州进行分组。

totals = grouped["contb_receipt_amt"].sum().unstack(level=0).fillna(0) 计算每个候选人在不同州的总捐款金额,并将结果转换为宽格式(以候选人姓名为列)。使用 sum() 方法计算总和,然后使用 unstack() 方法将结果转换为宽格式。最后,使用 fillna(0) 方法用 0 填充缺失值。

totals = totals[totals.sum(axis="columns") > 100000] 筛选出总捐款金额超过 100,000 的行。使用 sum(axis="columns") 方法计算每行的总和,再使用条件判断 totals.sum(axis="columns") > 100000 进行筛选。

totals.head(10) 显示前 10 行的 totals DataFrame。

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_nmObama, BarackRomney, Mitt
contbr_st
AK281840.1586204.24
AL543123.48527303.51
AR359247.28105556.00
AZ1506476.981888436.23
CA23824984.2411237636.60
CO2132429.491506714.12
CT2068291.263499475.45
DC4373538.801025137.50
DE336669.1482712.00
FL7318178.588338458.81

percent = totals.div(totals.sum(axis="columns"), axis="index") 对每个州的总捐款金额进行归一化,即计算每个州的捐款金额在总捐款金额中的比例。使用 div() 方法,将每行的总和作为除数,进行归一化计算。

percent.head(10) 显示前 10 行的 percent DataFrame,即每个州的归一化比例结果。

percent = totals.div(totals.sum(axis="columns"), axis="index")  # 对每个州的总捐款金额进行归一化,以每行的总和作为除数,计算归一化后的比例
 
percent.head(10)  # 显示前10行的percent DataFrame,即每个州的归一化比例结果
cand_nmObama, BarackRomney, Mitt

contbr_st

AK0.7657780.234222
AL0.5073900.492610
AR0.7729020.227098
AZ0.4437450.556255
CA0.6794980.320502
CO0.5859700.414030
CT0.3714760.628524
DC0.8101130.189887
DE0.8027760.197224
FL0.4674170.532583

四个实例就到此结束了

大家可以尝试回答以下问题,来检验自己是否掌握相关函数

#假设有以下DataFrame df:

df = pd.DataFrame({'Name': ['Alice', 'Bob', 'Charlie',
 'Alice', 'Bob', 'Charlie', 'Alice'], 
'Category': ['A', 'B', 'B', 'A', 'B', 'B', 'A'], 
'Value': [10, 20, 30, 40, 50, 60, 70]})

1.若使用pivot_table()函数对df进行操作,并以 'Name' 为行索引,'Category' 为列索引,对 'Value' 进行平均聚合计算,则结果DataFrame的形状将是:

A. 2行 × 2列          B. 3行 × 2列

C. 2行 × 3列           D. 3行 × 3列

2.在以下错误示例代码中,哪个会导致创建新列失败?

A) mean_ratings["diff"] = mean_ratings["column1"] + mean_ratings["column2"]

B) mean_ratings["diff"] = "column1" - "column2"

C) mean_ratings["diff"] = mean_ratings.column1 - mean_ratings.column2

3.当使用 sort_values() 函数进行降序排序时,以下哪个选项是正确的?

A. sort_values(ascending=True)

B. sort_values(descending=True)

C. sort_values(ascending=False)

D. sort_values(order='desc')

答案 1.B

         2.B、C

         3.C

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值