作者:chen_h 微信号 & QQ:862251340 微信公众号:coderpai
pandas系列学习(一):pandas入门
pandas系列学习(二):Series
pandas系列学习(三):DataFrame
如果你正在进行数据科学,从基于 Excel 的分析转向 Python 脚本和自动分析领域,你将会遇到非常流行的数据处理方式 Pandas。Pandas 的开发始于 2008 年,主要开发人员是 Wes McKinney,该库已经成为使用 Python 进行数据分析和管理的标准。对于任何基于 Python 的数据专业人士,pandas 都是必不可少的工具。
这篇文章的目的是帮助初学者掌握 pandas 的基本数据格式 —— DataFrame。我们将研究创建数据框架的基本方法,DataFrame 是如何工作的。
本文中的主题是以下内容:
将数据从文件加载到 Python Pandas DataFrame 中;检查数据的基本统计信息;修改一些数值;最后将结果输出到新文件;pandas 库将 DataFrame 定义为具有行和列的二维数据,大小可变的数据结构。简而言之,你可以将 DataFrame 视为数据表,即一维格式化的二维数据,它具有以下特征:
数据中可以有多个行和列;每行代表一个数据样本;每列包含描述样本的不同变量;每列中的数据通常是相同类型的数据 —— 例如,数字,字符串,日期;通常,与 Excel 数据集不同的是,DataFrame 避免丢失值,并且行或列之间没有间隙和空值;举例来说,以下数据集很适合 Pandas DataFrame:
在学校系统 DataFrame 中 —— 每行可以代表学校中的单个学生,列可以表示学生姓名(字符串),年龄(数字),出生日期(日期)和地址(字符串);在经济学数据框架中,每一行可以代表一个城市或者地理区域,列可能包括区域名称(字符串),人口(数量),人口平均年龄(数量),住户数量(数量),每个地区的学校数量(数量)等;在电子商务系统或者商店中,DataFrame 中的每一行都可用于表示客户,其中有购买商品数量(数量),原始注册日期(日期)和信用卡(字符串);我们将研究两种创建 DataFrame 的方法 —— 手动创建和逗号分隔值(CSV)文件。
每个数据科学项目的开始将包括将有用的数据导入分析环境,在本例中为 Python 。有多种方法可以在 Python 中创建 DataFrame 数据,最简单的犯法是手动将数据输入 Python,这显然只适用于微小的数据集。
data = {"column_1": [1,2,3,4,5], "another_column": ["this", "column", "has", "strings", "inside"], "float_column": [0.1,0.5,33,48,42.5558], "binary_solo": [True, False, True, True, False] } new_dataframe = pd.DataFrame(data) new_dataframe another_columnbinary_solocolumn_1float_column0thisTrue10.10001columnFalse20.50002hasTrue333.00003stringsTrue448.00004insideFalse542.5558使用 Python 词典和列表创建 DataFrame 仅适用于你可以手动输入的小型数据集。还有其他方法可以格式化手动输入的数据,你可以查看官网。
请注意,我们一般都是预定将 pandas 库加载为 pd,这种方式也是官网推荐的方式,也会我们日常习惯用到的方式。
一旦知道文件的路径,使用 pandas 中的 read_csv() 函数就可以非常简单的从 csv 文件创建 DataFrame 。csv 文件是包含表格形式数据的文本文件,其中列使用“,”逗号字符分割,行位于不同的行上。
如果你的数据是采用其他形式,例如 SQL数据库或者 Excel(XLS / XLSX)文件,则额可以查看其他函数以从这些源读取到 DataFrame 中,即 read_xlsx,read_sql 。但是,为简单起见,有时候最好将数据直接提取到 csv 然后再使用它们。
我们来举个例子,我们将从 Data Science 竞赛网站 kaggle 下载的数据来记性实验,你可以直接点击这个链接进行下载。这个数据的格式非常的好,你可以现在 Excel 中打开它进行预览:
样本数据包含 21478 行数据,每行对应于来自特定国家地区的食物来源,前 10 列代表样本国家和食品的信息,其余栏代表 1963 年至 2013 年每年的粮食产量(总共 63 列)。
接下来,我们可以使用 pandas 来来加载这个 csv 数据,如下所示:
path_to_file = './Downloads/FAO+database.csv' data = pd.read_csv(path_to_file, encoding='ISO-8859-1') print(type(data))在 Python 中有数据之后,你肯定希望看到数据已经加载,并确认存在预期的行和列。
如果你使用的是 Jupyter ,只需要输入数据库的名称即可获得输出良好的输出。打印是预览加载数据的便捷方式,你可以确认列名是否已经正确导入,数据格式是否符合预期,以及是否有任何缺失值。
data.head() Area AbbreviationArea CodeAreaItem CodeItemElement CodeElementUnitlatitudelongitude…Y2004Y2005Y2006Y2007Y2008Y2009Y2010Y2011Y2012Y20130AF2Afghanistan2511Wheat and products5142Food1000 tonnes33.9467.71…3249.03486.03704.04164.04252.04538.04605.04711.0481048951AF2Afghanistan2805Rice (Milled Equivalent)5142Food1000 tonnes33.9467.71…419.0445.0546.0455.0490.0415.0442.0476.04254222AF2Afghanistan2513Barley and products5521Feed1000 tonnes33.9467.71…58.0236.0262.0263.0230.0379.0315.0203.03673603AF2Afghanistan2513Barley and products5142Food1000 tonnes33.9467.71…185.043.044.048.062.055.060.072.078894AF2Afghanistan2514Maize and products5521Feed1000 tonnes33.9467.71…120.0208.0233.0249.0247.0195.0178.0191.0200200shape 命令提供有关于数据集大小的信息 —— shape 返回一个包含行数的元祖,以及 DataFrame 中数据的列数。另一个描述属性是 ‘ndim’,它给出了数据中的维数,通常为 2 。
data.shape (21477, 63) data.ndim 2从上面的结果中,我们可以看到我们的食品生产数据包含 21477 行,每行有 63 列,如 .shape 的输出所示。我们有两个维度 —— 即具有高度和宽度的2D数据帧。如果你的数据只有一列,则 ndim 将返回 1。
默认情况下,DataFrame.head() 函数向你显示 DataFrame 中的前5行数据,相反的是 DataFrame.tail() 函数向你显示 DataFrame 中的最后5行数据。
如果你想打印特定的行数,那么你只需要向 head() 和 tail() 函数中传入特定的数字就行了。比如你想打印最开始的 10 行数据,那么你只需要调用 head(10) 就可以打印最开始的10行数据了。
data.head() Area AbbreviationArea CodeAreaItem CodeItemElement CodeElementUnitlatitudelongitude…Y2004Y2005Y2006Y2007Y2008Y2009Y2010Y2011Y2012Y20130AF2Afghanistan2511Wheat and products5142Food1000 tonnes33.9467.71…3249.03486.03704.04164.04252.04538.04605.04711.0481048951AF2Afghanistan2805Rice (Milled Equivalent)5142Food1000 tonnes33.9467.71…419.0445.0546.0455.0490.0415.0442.0476.04254222AF2Afghanistan2513Barley and products5521Feed1000 tonnes33.9467.71…58.0236.0262.0263.0230.0379.0315.0203.03673603AF2Afghanistan2513Barley and products5142Food1000 tonnes33.9467.71…185.043.044.048.062.055.060.072.078894AF2Afghanistan2514Maize and products5521Feed1000 tonnes33.9467.71…120.0208.0233.0249.0247.0195.0178.0191.02002005 rows × 63 columns
data.tail() Area AbbreviationArea CodeAreaItem CodeItemElement CodeElementUnitlatitudelongitude…Y2004Y2005Y2006Y2007Y2008Y2009Y2010Y2011Y2012Y201321472ZW181Zimbabwe2948Milk - Excluding Butter5142Food1000 tonnes-19.0229.15…373.0357.0359.0356.0341.0385.0418.0457.042645121473ZW181Zimbabwe2960Fish, Seafood5521Feed1000 tonnes-19.0229.15…5.04.09.06.09.05.015.015.0151521474ZW181Zimbabwe2960Fish, Seafood5142Food1000 tonnes-19.0229.15…18.014.017.014.015.018.029.040.0404021475ZW181Zimbabwe2961Aquatic Products, Other5142Food1000 tonnes-19.0229.15…0.00.00.00.00.00.00.00.00021476ZW181Zimbabwe2928Miscellaneous5142Food1000 tonnes-19.0229.15…0.00.00.00.00.00.00.00.0005 rows × 63 columns
data.tail(10) Area AbbreviationArea CodeAreaItem CodeItemElement CodeElementUnitlatitudelongitude…Y2004Y2005Y2006Y2007Y2008Y2009Y2010Y2011Y2012Y201321467ZW181Zimbabwe2943Meat5142Food1000 tonnes-19.0229.15…222.0228.0233.0238.0242.0265.0262.0277.028025821468ZW181Zimbabwe2945Offals5142Food1000 tonnes-19.0229.15…20.020.021.021.021.021.021.021.0222221469ZW181Zimbabwe2946Animal fats5142Food1000 tonnes-19.0229.15…26.026.029.029.027.031.030.025.0262021470ZW181Zimbabwe2949Eggs5142Food1000 tonnes-19.0229.15…15.018.018.021.022.027.027.024.0242521471ZW181Zimbabwe2948Milk - Excluding Butter5521Feed1000 tonnes-19.0229.15…21.021.021.021.021.023.025.025.0303121472ZW181Zimbabwe2948Milk - Excluding Butter5142Food1000 tonnes-19.0229.15…373.0357.0359.0356.0341.0385.0418.0457.042645121473ZW181Zimbabwe2960Fish, Seafood5521Feed1000 tonnes-19.0229.15…5.04.09.06.09.05.015.015.0151521474ZW181Zimbabwe2960Fish, Seafood5142Food1000 tonnes-19.0229.15…18.014.017.014.015.018.029.040.0404021475ZW181Zimbabwe2961Aquatic Products, Other5142Food1000 tonnes-19.0229.15…0.00.00.00.00.00.00.00.00021476ZW181Zimbabwe2928Miscellaneous5142Food1000 tonnes-19.0229.15…0.00.00.00.00.00.00.00.00010 rows × 63 columns
许多 DataFrame 都有混合数据类型,也就是说,有些列是数字,有些是字符串,有些是日期等。在内部,csv 文件不包含每列中包含哪些数据类型的信息;所有数据都只是字符。pandas 在加载数据时推断数据类型,例如,如果列只包含数字,则 pandas 会将该列的数据类型设置为 numeric:integer 或者 float。
你可以使用数据框的 .dtypes 属性来检查示例中每列的类型。
data.dtypes Area Abbreviation object Area Code int64 Area object Item Code int64 Item object Element Code int64 Element object Unit object latitude float64 longitude float64 Y1961 float64 Y1962 float64 Y1963 float64 Y1964 float64 Y1965 float64 Y1966 float64 Y1967 float64 Y1968 float64 Y1969 float64 Y1970 float64 Y1971 float64 Y1972 float64 Y1973 float64 Y1974 float64 Y1975 float64 Y1976 float64 Y1977 float64 Y1978 float64 Y1979 float64 Y1980 float64 ....在某些情况下,自动推断数据类型可能会产生意外结果。请注意,字符串作为“对象”数据类型加载,要更改特定列的数据类型,请使用 .astype() 函数。例如,要将 “项目代码” 列视为字符串,请使用:
data['Item Code'].astype(str)最后,要查看有关特定列的一些核心统计信息,我们可以使用 describe() 函数。
对于数字列,describe() 返回基本统计信息:列中数据的值计数,平均值,标准差,最小值,最大值以及第 25,第 50和第75的中位数;对于字符串列,describe() 返回值计数,唯一条目数,最常出现的值(top value) 以及最高值出现的次数(freq);利用 [] 选择要进行描述的列,并调用 describe() ,如下所示:
data['Y2013'].describe() count 21477.000000 mean 575.557480 std 6218.379479 min -246.000000 25% 0.000000 50% 8.000000 75% 90.000000 max 489299.000000 Name: Y2013, dtype: float64 data['Area'].describe() count 21477 unique 174 top Spain freq 150 Name: Area, dtype: object使用 describe() 函数获取 DataFrame 中列的基本统计信息。请注意具有 numeric 数据类型的列与字符串和字符列之间的差异。
请注意,如果在整个 DataFrame 上调用 describe,则仅返回具有 numeric 数据类型的列的统计信息,并返回 DataFrame 格式。
data.describe() Area CodeItem CodeElement CodelatitudelongitudeY1961Y1962Y1963Y1964Y1965…Y2004Y2005Y2006Y2007Y2008Y2009Y2010Y2011Y2012Y2013count21477.00000021477.00000021477.00000021477.00000021477.00000017938.00000017938.00000017938.00000017938.00000017938.000000…21128.00000021128.00000021373.00000021373.00000021373.00000021373.00000021373.00000021373.00000021477.00000021477.000000mean125.4494112694.2115295211.68715420.45061315.794445195.262069200.782250205.464600209.925577217.556751…486.690742493.153256496.319328508.482104522.844898524.581996535.492069553.399242560.569214575.557480std72.868149148.973406146.82007924.62833666.0121041864.1243361884.2655911861.1747391862.0001162014.934333…5001.7820085100.0570365134.8193735298.9398075496.6975135545.9393035721.0894255883.0716046047.9508046218.379479min1.0000002511.0000005142.000000-40.900000-172.1000000.0000000.0000000.0000000.0000000.000000…0.0000000.0000000.0000000.0000000.0000000.0000000.0000000.000000-169.000000-246.00000025%63.0000002561.0000005142.0000006.430000-11.7800000.0000000.0000000.0000000.0000000.000000…0.0000000.0000000.0000000.0000000.0000000.0000000.0000000.0000000.0000000.00000050%120.0000002640.0000005142.00000020.59000019.1500001.0000001.0000001.0000001.0000001.000000…6.0000006.0000007.0000007.0000007.0000007.0000007.0000008.0000008.0000008.00000075%188.0000002782.0000005142.00000041.15000046.87000021.00000022.00000023.00000024.00000025.000000…75.00000077.00000078.00000080.00000082.00000083.00000083.00000086.00000088.00000090.000000max276.0000002961.0000005521.00000064.960000179.410000112227.000000109130.000000106356.000000104234.000000119378.000000…360767.000000373694.000000388100.000000402975.000000425537.000000434724.000000451838.000000462696.000000479028.000000489299.0000008 rows × 58 columns
describe() 最后返回的是一个统计信息,格式是另一个 DataFrame 。
pandas 的数据选择方法非常灵活。在本文章中,我们来查看列和行的基本操作。
在 pandas 中选择列有三种主要方式:
使用点符号,例如,data.column_name;使用方括号和列的名称作为字符串,例如 data[‘column_name’];使用数字索引和 iloc 选择器 data.iloc[:, <column_number>]; data.Area.head() 0 Afghanistan 1 Afghanistan 2 Afghanistan 3 Afghanistan 4 Afghanistan Name: Area, dtype: object data['Area'].head() 0 Afghanistan 1 Afghanistan 2 Afghanistan 3 Afghanistan 4 Afghanistan Name: Area, dtype: object data.iloc[:,2].head() 0 Afghanistan 1 Afghanistan 2 Afghanistan 3 Afghanistan 4 Afghanistan Name: Area, dtype: object使用任何这些方法选择列时,最后生成的都是 Series 数据类型。pandas Series 是一维数据结构。了解可以对这些 Series 数据执行的基本操作是非常有用的,包括求和( .sum() ),求平均值( .mean() ),计数( .count() ),得到中位数( .median() ),并替换缺失值( .fillna(new_value) )。
[data['Y2007'].sum(), # Total sum of the column values data['Y2007'].mean(), # Mean of the column values data['Y2007'].median(), # Median of the column values data['Y2007'].nunique(), # Number of unique entries data['Y2007'].max(), # Maximum of the column values data['Y2007'].min()] # Minimum of the column values [10867788.0, 508.48210358863986, 7.0, 1994, 402975.0, 0.0]同时选择多个列会从现有 DataFrame 中提取新的 DataFrame 。要选择多列,语法为:
带有列名列表的方括号选择,例如:data[ [ ‘column_name_1’, ‘column_name_2’ ] ];使用带有 iloc 选择器的数字索引和列号列表,例如:data.iloc[:, [0,1,3,4]]; data[['Area Code', 'Area']].head() Area CodeArea02Afghanistan12Afghanistan22Afghanistan32Afghanistan42Afghanistan data.iloc[:,[1,2]].head() Area CodeArea02Afghanistan12Afghanistan22Afghanistan32Afghanistan42Afghanistan通常使用 iloc / loc 选择方法或使用逻辑选择器(基于另一列或者变量的值进行选择)来选择 DataFrame 中的行。以下是一些基本选择行的方式:
使用 iloc 选择器进行数字选择,例如 data.iloc[0:10, : ] ,这就能选择前 10 行;使用 loc 选择器进行基于标签的行选择,例如 data.loc[2, : ];使用评估语句的基于逻辑的行选择,例如 data[ data[ “Area” ] == “Ireland” ] 选择 Area 值为 Ireland 的行; data.iloc[[1,2], : ].head() Area AbbreviationArea CodeAreaItem CodeItemElement CodeElementUnitlatitudelongitude…Y2004Y2005Y2006Y2007Y2008Y2009Y2010Y2011Y2012Y20131AF2Afghanistan2805Rice (Milled Equivalent)5142Food1000 tonnes33.9467.71…419.0445.0546.0455.0490.0415.0442.0476.04254222AF2Afghanistan2513Barley and products5521Feed1000 tonnes33.9467.71…58.0236.0262.0263.0230.0379.0315.0203.03673602 rows × 63 columns
data.loc[2, : ] Area Abbreviation AF Area Code 2 Area Afghanistan Item Code 2513 Item Barley and products Element Code 5521 Element Feed Unit 1000 tonnes latitude 33.94 longitude 67.71 Y1961 76 .... data[ data["Area"] == 'Ireland' ].head() Area AbbreviationArea CodeAreaItem CodeItemElement CodeElementUnitlatitudelongitude…Y2004Y2005Y2006Y2007Y2008Y2009Y2010Y2011Y2012Y20139533IE104Ireland2511Wheat and products5521Feed1000 tonnes53.41-8.24…968.0976.0902.0685.01063.0804.0783.0760.06506009534IE104Ireland2511Wheat and products5142Food1000 tonnes53.41-8.24…395.0423.0501.0449.0470.0493.0512.0502.04945009535IE104Ireland2805Rice (Milled Equivalent)5521Feed1000 tonnes53.41-8.24…3.03.03.04.05.04.04.04.0449536IE104Ireland2805Rice (Milled Equivalent)5142Food1000 tonnes53.41-8.24…11.06.06.09.014.015.016.014.014149537IE104Ireland2513Barley and products5521Feed1000 tonnes53.41-8.24…993.0908.01047.0904.01242.01290.01283.01182.0114613805 rows × 63 columns
我们可以灵活使用多种方式对行和列的组合选择,以实现对数据的操作。
要从 DataFrame 中删除行和列,pandas 给我们准备了 drop 函数。
要删除一列或者多列,请使用列的名称,并且将轴(axis)指定为 1。或者,如下面的例子所示,在 pandas 中添加了 “columns” 参数,从而不需要指定轴。drop 函数返回的是一个新的 DataFrame,并且删除了列。如果你需要编辑原始 DataFrame,可以将 inplace 参数设置为 True,并且没有返回值。
# Deleting columns # Delete the "Area" column from the dataframe data = data.drop("Area", axis=1) # alternatively, delete columns using the columns parameter of drop data = data.drop(columns="area") # Delete the Area column from the dataframe in place # Note that the original 'data' object is changed when inplace=True data.drop("Area", axis=1, inplace=True). # Delete multiple columns from the dataframe data = data.drop(["Y2001", "Y2002", "Y2003"], axis=1)也可以使用 drop 函数删除行,方法是指定 axis = 0。drop() 根据标签删除行,而不是数字索引,要根据数字位置 / 索引删除行,请使用 iloc 重新分配数据框值,如下所示:
data.head(3) Area AbbreviationArea CodeAreaItem CodeItemElement CodeElementUnitlatitudelongitude…Y2004Y2005Y2006Y2007Y2008Y2009Y2010Y2011Y2012Y20130AF2Afghanistan2511Wheat and products5142Food1000 tonnes33.9467.71…3249.03486.03704.04164.04252.04538.04605.04711.0481048951AF2Afghanistan2805Rice (Milled Equivalent)5142Food1000 tonnes33.9467.71…419.0445.0546.0455.0490.0415.0442.0476.04254222AF2Afghanistan2513Barley and products5521Feed1000 tonnes33.9467.71…58.0236.0262.0263.0230.0379.0315.0203.03673603 rows × 63 columns
data.drop([0,1], axis=0).head(3) Area AbbreviationArea CodeAreaItem CodeItemElement CodeElementUnitlatitudelongitude…Y2004Y2005Y2006Y2007Y2008Y2009Y2010Y2011Y2012Y20132AF2Afghanistan2513Barley and products5521Feed1000 tonnes33.9467.71…58.0236.0262.0263.0230.0379.0315.0203.03673603AF2Afghanistan2513Barley and products5142Food1000 tonnes33.9467.71…185.043.044.048.062.055.060.072.078894AF2Afghanistan2514Maize and products5521Feed1000 tonnes33.9467.71…120.0208.0233.0249.0247.0195.0178.0191.02002003 rows × 63 columns
Pandas 中的 drop() 函数用于从 DataFrame 中删除行,轴设置为 0。如前所述,inplace 参数可用于更改 DataFrame 而无需重新分配。
# Delete the rows with labels 0,1,5 data = data.drop([0,1,2], axis=0) # Delete the rows with label "Ireland" # For label-based deletion, set the index first on the dataframe: data = data.set_index("Area") data = data.drop("Ireland", axis=0). # Delete all rows with label "Ireland" # Delete the first five rows using iloc selector data = data.iloc[5:,]使用 DataFrame 重命名功能可以在 pandas 中轻松实现列重命名。重命名功能易于使用,而且非常灵活。以这两种方式重命名列:
通过使用字典将旧名称映射到新名称进行重命名,格式为 {“old_column_name”: “new_column_name”, …};通过提供更改列名称的函数重命名。函数应用于每个列名称。 # Rename columns using a dictionary to map values # Rename the Area columnn to 'place_name' data = data.rename(columns={"Area": "place_name"}) # Again, the inplace parameter will change the dataframe without assignment data.rename(columns={"Area": "place_name"}, inplace=True) # Rename multiple columns in one go with a larger dictionary data.rename( columns={ "Area": "place_name", "Y2001": "year_2001" }, inplace=True ) # Rename all columns using a function, e.g. convert all column names to lower case: data.rename(columns=str.lower)在许多情况下,我使用列名称的整理函数来确保变量名称的标准 camel-case 格式。从可能非结构化数据集加载数据时,使用 lambda 函数删除空格和小写所有列名称会很有用:
# Quickly lowercase and camelcase all column names in a DataFrame data = pd.read_csv("/path/to/csv/file.csv") data.rename(columns=lambda x: x.lower().replace(' ', '_'))在操作或者计算之后,下一步是将数据保存回 csv 文件,pandas 中的数据输出就像加载数据一样简单。
你只需要知道两个函数:第一个 to_csv 函数将 DataFrame 写入 csv 文件,to_excel 函数将 DataFrame 信息写入 Microsoft Excel 文件。
# Output data to a CSV file # Typically, I don't want row numbers in my output file, hence index=False. # To avoid character issues, I typically use utf8 encoding for input/output. data.to_csv("output_filename.csv", index=False, encoding='utf8') # Output data to an Excel file. # For the excel output to work, you may need to install the "xlsxwriter" package. data.to_csv("output_excel_file.xlsx", sheet_name="Sheet 1", index=False)加载数据之后,你需要将其按一个或另一个值分组,然后运行一些计算。这个我们会在后续文章 中介绍。
pandas 内置了一个相对广泛的绘图功能,可用于初步图形化探索 —— 尤其是当你使用 Jupyter 进行数据分析。
你需要安装 matplotlib 绘图包以生成图形,并且导入 matplotlib.pyplot 作为 plt,以便为图标添加图形标签和轴标签。pandas 原生的 plot() 命令提供了大量功能。
import matplotlib.pyplot as plt data['latitude'].plot(kind='hist', bins=100) plt.xlabel('Latitude Value') plt.show() plot_data = data[data["Element"] == 'Food'] plot_data = plot_data.groupby('Area')['Y2013'].sum() plot_data.sort_values()[-10:].plot(kind='bar') plt.title("Top Ten Food Producers") plt.ylabel("Food produced (tonnes)") plt.show()使用 pandas DataFrame 绘图命令,结合数据分析,数据分组和最终绘图。
