Pandas 数据类型概览
简介
在进行数据分析时,确保使用正确的数据类型非常重要,否则可能会得到意想不到的结果或错误。对 Pandas 而言,它会在很多情况下正确地作出数据类型推断,你可以继续进行分析工作,而无需深入思考该主题。
尽管 Pandas 工作得很好,但在数据分析过程中的某个时刻,你可能需要将数据从一种类型显式转换为另一种类型。本文将讨论 Pandas 的基本数据类型(即 dtypes),它们如何映射到 python 和 numpy 数据类型,以及从一种 Pandas 类型转换为另一种类型的几个方式。
Pandas 的数据类型
数据类型本质上是编程语言用来理解如何存储和操作数据的内部结构。例如,一个程序需要理解你可以将两个数字加起来,比如 5 + 10 得到 15。或者,如果是两个字符串,比如「cat」和「hat」,你可以将它们连接(加)起来得到「cathat」。
有关 Pandas 数据类型的一个可能令人困惑的地方是,Pandas、Python 和 numpy 的数据类型之间有一些重叠。下表总结了关键点:
Pandas dtype 映射:
Pandas dtype | Python 类型 | NumPy 类型 | 用途 |
object | str | string, unicode | 文本 |
int64 | int | int_, int8, int16, int32, int64, uint8, uint16, uint32, uint64 | 整数 |
float64 | float | float_, float16, float32, float64 | 浮点数 |
bool | bool | bool_ | 布尔值 |
datetime64 | NA | NA | 日期时间 |
timedelta[ns] | NA | NA | 时间差 |
category | NA | NA | 有限长度的文本值列表 |
大多数情况下,你不必担心是否应该明确地将pandas类型强制转换为对应的 NumPy 类型。一般来说使用 Pandas 的默认 int64 和 float64 就可以。列出此表的唯一原因是,有时你可能会在代码行间或自己的分析过程中看到 Numpy 的类型。
对于本文,我将重点关注以下 Pandas 类型:
- object
- int64
- float64
- datetime64
- bool
如果你有兴趣,我会再写一篇文章来专门介绍 category 和 timedelta 类型。不过本文中概述的基本方法也适用于这些类型。
我们为什么关心类型?
数据类型是在你遇到错误或意外结果之前并不会关心的事情之一。不过当你将新数据加载到 Pandas 进行进一步分析时,这也是你应该检查的第一件事情。
我将使用一个非常简单的 CSV文件 来说明在 Pandas 中可能会遇到的一些常见的由数据类型导致的错误。另外,在 github 上也一个示例 notbook。
import numpy as np
import pandas as pd
df = pd.read_csv("sales_data_types.csv")
Customer Number | Customer Name | 2016 | 2017 | Percent Growth | Jan Units | Month | Day | Year | Active | |
0 | 10002.0 | Quest Industries | $125,000.00 | $162500.00 | 30.00% | 500 | 1 | 10 | 2015 | Y |
1 | 552278.0 | Smith Plumbing | $920,000.00 | $101,2000.00 | 10.00% | 700 | 6 | 15 | 2014 | Y |
2 | 23477.0 | ACME Industrial | $50,000.00 | $62500.00 | 25.00% | 125 | 3 | 29 | 2016 | Y |
3 | 24900.0 | Brekke LTD | $350,000.00 | $490000.00 | 4.00% | 75 | 10 | 27 | 2015 | Y |
4 | 651029.0 | Harbor Co | $15,000.00 | $12750.00 | -15.00% | Closed | 2 | 2 | 2014 | N |
乍一看,数据没什么问题,所以我们可以尝试做一些操作来分析数据。我们来试一下把 2016 和 2017 年的销售额加起来:
df['2016'] + df['2017']
0 $125,000.00$162500.00
1 $920,000.00$101,2000.00
2 $50,000.00$62500.00
3 $350,000.00$490000.00
4 $15,000.00$12750.00
dtype: object
这看起来就不对了。我们希望将总计加在一起,但 Pandas 只是将这两个值连接在一起创建了一个长字符串。这个问题的一个线索是 dtype:object。object 在 Pandas 代表字符串,所以它执行的是字符串操作而不是数学操作。
如果我们想查看 dataframe 中的所有数据类型,使用 df.dtypes
df.dtypes
Customer Number float64
Customer Name object
2016 object
2017 object
Percent Growth object
Jan Units object
Month int64
Day int64
Year int64
Active object
dtype: object
此外,df.info() 函数可以显示更有用的信息。
df.info()
<class 'pandas.core.frame.DataFrame'>
RangeIndex: 5 entries, 0 to 4
Data columns (total 10 columns):
Customer Number 5 non-null float64
Customer Name 5 non-null object
2016 5 non-null object
2017 5 non-null object
Percent Growth 5 non-null object
Jan Units 5 non-null object
Month 5 non-null int64
Day 5 non-null int64
Year 5 non-null int64
Active 5 non-null object
dtypes: float64(1), int64(3), object(6)
memory usage: 480.0+ bytes
查看自动分配的数据类型后,有几个问题:
- Customer Number 被归为 float64 但它应该是 int64
- 2016 和 2017 这两列被存储为 object,但应该是 float64 或 int64 这样的数值类型
- Percent Growth 和 Jan Units 也被存储为 object 而不是数值类型
- Month、Day 和 Year 这三列应该被转换为 datetime64
- Active 列应该是布尔型
在我们清洗这些数据类型之前,要对这些数据做更多的附加分析是非常困难的。
为了在 Pandas 中转换数据类型,有三个基本选项:
- 使用 astype() 来强制转换到合适的 dtype
- 创建一个自定义函数来转换数据
- 使用 Pandas 的函数,例如 to_numeric() 或 to_datetime()
使用 astype() 函数
将 Pandas 数据列转换为不同类型的最简单方法就是用 astype()。例如,要将 Customer Number 转换为整数,我们可以这样调用:
df['Customer Number'].astype('int')
0 10002
1 552278
2 23477
3 24900
4 651029
Name: Customer Number, dtype: int64
为了真正修改原始 dataframe 中的客户编号(Customer Number),记得把 astype() 函数的返回值重新赋值给 dataframe,因为 astype() 仅返回数据的副本而不原地修改。
df["Customer Number"] = df['Customer Number'].astype('int')
df.dtypes
Customer Number int64
Customer Name object
2016 object
2017 object
Percent Growth object
Jan Units object
Month int64
Day int64
Year int64
Active object
dtype: object
以下是客户编号(Customer Number)为整数的新 dataframe:
Customer Number | Customer Name | 2016 | 2017 | Percent Growth | Jan Units | Month | Day | Year | Active | |
0 | 10002 | Quest Industries | $125,000.00 | $162500.00 | 30.00% | 500 | 1 | 10 | 2015 | Y |
1 | 552278 | Smith Plumbing | $920,000.00 | $101,2000.00 | 10.00% | 700 | 6 | 15 | 2014 | Y |
2 | 23477 | ACME Industrial | $50,000.00 | $62500.00 | 25.00% | 125 | 3 | 29 | 2016 | Y |
3 | 24900 | Brekke LTD | $350,000.00 | $490000.00 | 4.00% | 75 | 10 | 27 | 2015 | Y |
4 | 651029 | Harbor Co | $15,000.00 | $12750.00 | -15.00% | Closed | 2 | 2 | 2014 | N |
这一切看起来不错,并且似乎很简单。让我们尝试对 2016 列做同样的事情并将其转换为浮点数:
df['2016'].astype('float')
ValueError Traceback (most recent call last)
<ipython-input-45-999869d577b0> in <module>()
----> 1 df['2016'].astype('float')
[一些错误信息]
ValueError: could not convert string to float: '$15,000.00'
以类似的方式,我们可以尝试将 Jan Units 列转换为整数:
df['Jan Units'].astype('int')
ValueError Traceback (most recent call last)
<ipython-input-44-31333711e4a4> in <module>()
----> 1 df['Jan Units'].astype('int')
[一些错误信息]
ValueError: invalid literal for int() with base 10: 'Closed'
这两个都返回 ValueError 异常,这意味着转换不起作用。
在这两个例子中,数据都包含不能被解释为数字的值。在销售额(2016)列中,数据包括货币符号以及每个值中的逗号。在 Jan Units 列中,最后一个值是 "Closed",它不是一个数字;所以我们得到了异常。
到目前为止,astype() 作为工具看起来并不怎么好。我们在 Active 列中再试一次。
df['Active'].astype('bool')
0 True
1 True
2 True
3 True
4 True
Name: Active, dtype: bool
第一眼,这看起来不错,但经过仔细检查,存在一个大问题。所有的值都被解释为 True,但最后一个客户有一个 N 的活动(Active)标志,所以这并不正确。
这一节的重点是 astype() 只有在下列情况下才能工作:
- 数据是干净的,可以简单地解释为一个数字
- 你想要将一个数值转换为一个字符串对象
如果数据具有非数字字符或它们间不同质(homogeneous),那么 astype() 并不是类型转换的好选择。你需要进行额外的变换才能完成正确的类型转换。