- 原文地址:Overview of Pandas Data Types
- 原文做者:Chris Moffitt
- 译文出自:掘金翻译计划
- 本文永久连接:github.com/xitu/gold-m…
- 译者:stormluke
- 校对者:Starrier、luochen1992
在进行数据分析时,确保使用正确的数据类型很是重要,不然可能会获得意想不到的结果或错误。对 Pandas 而言,它会在不少状况下正确地做出数据类型推断,你能够继续进行分析工做,而无需深刻思考该主题。html
尽管 Pandas 工做得很好,但在数据分析过程当中的某个时刻,你可能须要将数据从一种类型显式转换为另外一种类型。本文将讨论 Pandas 的基本数据类型(即 dtypes
),它们如何映射到 python 和 numpy 数据类型,以及从一种 Pandas 类型转换为另外一种类型的几个方式。前端
数据类型本质上是编程语言用来理解如何存储和操做数据的内部结构。例如,一个程序须要理解你能够将两个数字加起来,好比 5 + 10 获得 15。或者,若是是两个字符串,好比「cat」和「hat」,你能够将它们链接(加)起来获得「cathat」。python
有关 Pandas 数据类型的一个可能使人困惑的地方是,Pandas、Python 和 numpy 的数据类型之间有一些重叠。下表总结了关键点:android
Pandas dtype
映射:ios
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 | 有限长度的文本值列表 |
大多数状况下,你没必要担忧是否应该明确地将熊猫类型强制转换为对应的 NumPy 类型。通常来讲使用 Pandas 的默认 int64
和 float64
就能够。我列出此表的惟一缘由是,有时你可能会在代码行间或本身的分析过程当中看到 Numpy 的类型。git
对于本文,我将重点关注如下 Pandas 类型:github
object
int64
float64
datetime64
bool
若是你有兴趣,我会再写一篇文章来专门介绍 category
和 timedelta
类型。不过本文中概述的基本方法也适用于这些类型。编程
数据类型是在你遇到错误或意外结果以前并不会关心的事情之一。不过当你将新数据加载到 Pandas 进行进一步分析时,这也是你应该检查的第一件事情。后端
我将使用一个很是简单的 CSV文件 来讲明在 Pandas 中可能会遇到的一些常见的由数据类型致使的错误。另外,在 github 上也一个示例 notbook。bash
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
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()
并非类型转换的好选择。你须要进行额外的变换才能完成正确的类型转换。
因为该数据转换稍微复杂一些,所以咱们能够构建一个自定义函数,将其应用于每一个值并转换为适当的数据类型。
对于货币转换(这个特定的数据集),下面是一个咱们可使用的简单函数:
def convert_currency(val):
""" Convert the string number value to a float - Remove $ - Remove commas - Convert to float type """
new_val = val.replace(',','').replace('$', '')
return float(new_val)
复制代码
该代码使用python的字符串函数去掉 $
和 ,
,而后将该值转换为浮点数。在这个特定状况下,咱们能够将值转换为整数,但我选择在这种状况下使用浮点数。
我也怀疑有人会建议咱们对货币使用 Decimal
类型。这不是 Pandas 的本地数据类型,因此我故意坚持使用 float 方式。
另外值得注意的是,该函数将数字转换为 python 的 float
,但 Pandas 内部将其转换为 float64
。正如前面提到的,我建议你容许 Pandas 在肯定合适的时候将其转换为特定的大小 float
或 int
。你不须要尝试将其转换为更小或更大的字节大小,除非你真的知道为何须要那样作。
如今,咱们可使用 Pandas 的 apply
函数将其应用于 2016 列中的全部值。
df['2016'].apply(convert_currency)
复制代码
0 125000.0
1 920000.0
2 50000.0
3 350000.0
4 15000.0
Name: 2016, dtype: float64
复制代码
成功!全部的值都显示为 float64
,咱们能够完成所须要的全部数学计算了。
我确信有经验的读者会问为何我不使用 lambda 函数?在回答以前,先看下咱们能够在一行中使用 lambda
函数完成的操做:
df['2016'].apply(lambda x: x.replace('$', '').replace(',', '')).astype('float')
复制代码
使用 lambda
,咱们能够将代码简化为一行,这是很是有效的方法。但我对这种方法有三个主要的意见:
read_csv()
时轻松清洗数据。我将在文章结尾处介绍具体的使用方法。有些人也可能会争辩说,其余基于 lambda 的方法比自定义函数的性能有所提升。但为了教导新手,我认为函数方法更好。
如下是使用 convert_currency
函数转换两个销售(2016 / 2017)列中数据的完整示例。
df['2016'] = df['2016'].apply(convert_currency)
df['2017'] = df['2017'].apply(convert_currency)
df.dtypes
复制代码
Customer Number int64
Customer Name object
2016 float64
2017 float64
Percent Growth object
Jan Units object
Month int64
Day int64
Year int64
Active object
dtype: object
复制代码
有关使用 lambda
和函数的另外一个例子,咱们能够看看修复 Percent Growth
列的过程。
使用 lambda
:
df['Percent Growth'].apply(lambda x: x.replace('%', '')).astype('float') / 100
复制代码
用自定义函数作一样的事情:
def convert_percent(val):
""" Convert the percentage string to an actual floating point percent - Remove % - Divide by 100 to make decimal """
new_val = val.replace('%', '')
return float(new_val) / 100
df['Percent Growth'].apply(convert_percent)
复制代码
二者返回的值相同:
0 0.30
1 0.10
2 0.25
3 0.04
4 -0.15
Name: Percent Growth, dtype: float64
复制代码
我将介绍的最后一个自定义函数是使用 np.where()
将活动(Active)列转换为布尔值。有不少方法来解决这个特定的问题。np.where()
方法对于不少类型的问题都颇有用,因此我选择在这里介绍它。
其基本思想是使用 np.where()
函数将全部 Y
值转换为 True
,其余全部值为 False
df["Active"] = np.where(df["Active"] == "Y", True, False)
复制代码
其结果以下 dataframe:
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 | True |
1 | 552278.0 | Smith Plumbing | $920,000.00 | $101,2000.00 | 10.00% | 700 | 6 | 15 | 2014 | True |
2 | 23477.0 | ACME Industrial | $50,000.00 | $62500.00 | 25.00% | 125 | 3 | 29 | 2016 | True |
3 | 24900.0 | Brekke LTD | $350,000.00 | $490000.00 | 4.00% | 75 | 10 | 27 | 2015 | True |
4 | 651029.0 | Harbor Co | $15,000.00 | $12750.00 | -15.00% | Closed | 2 | 2 | 2014 | False |
dtype 被正确地设置为了 bool
。
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 bool
dtype: object
复制代码
不管你选择使用 lambda
函数,仍是建立一个更标准的 Python 函数,或者是使用其余方法(如 np.where
),这些方法都很是灵活,而且能够根据你本身独特的数据需求进行定制。
Pandas 在直白的 astype()
函数和复杂的自定义函数之间有一个中间地带。这些辅助函数对于某些数据类型转换很是有用。
若是你顺序读下来,你会注意到我没有对日期列或 Jan Units
列作任何事情。这两种列均可以使用 Pandas 的内置函数(如 pd.to_numeric()
和 pd.to_datetime()
)进行转换。
Jan Units
转换出现问题的缘由是列中包含一个非数字值。若是咱们尝试使用 astype()
,咱们会获得一个错误(如前所述)。pd.to_numeric()
函数能够更优雅地处理这些值:
pd.to_numeric(df['Jan Units'], errors='coerce')
复制代码
0 500.0
1 700.0
2 125.0
3 75.0
4 NaN
Name: Jan Units, dtype: float64
复制代码
这里有几个值得注意的地方。首先,该函数轻松地处理了数据并建立了一个 float64
列。 此外,它会用 NaN
值替换无效的 Closed
值,由于咱们配置了 errors=coerce
。咱们能够将 Nan
留在那里,也可使用 fillna(0)
来用 0 填充:
pd.to_numeric(df['Jan Units'], errors='coerce').fillna(0)
复制代码
0 500.0
1 700.0
2 125.0
3 75.0
4 0.0
Name: Jan Units, dtype: float64
复制代码
我最终介绍的转换是将单独的月份、日期和年份列转换为到一个 datetime
类型的列。Pandas 的 pd.to_datetime()
函数 可定制性很好,但默认状况下也十分明智。
pd.to_datetime(df[['Month', 'Day', 'Year']])
复制代码
0 2015-01-10
1 2014-06-15
2 2016-03-29
3 2015-10-27
4 2014-02-02
dtype: datetime64[ns]
复制代码
在这种状况下,函数将这些列组合成适当 datateime64
dtype 的新列。
咱们须要确保将这些值赋值回 dataframe:
df["Start_Date"] = pd.to_datetime(df[['Month', 'Day', 'Year']])
df["Jan Units"] = pd.to_numeric(df['Jan Units'], errors='coerce').fillna(0)
复制代码
Customer Number | Customer Name | 2016 | 2017 | Percent Growth | Jan Units | Month | Day | Year | Active | Start_Date | |
---|---|---|---|---|---|---|---|---|---|---|---|
0 | 10002 | Quest Industries | 125000.0 | 162500.0 | 0.30 | 500.0 | 1 | 10 | 2015 | True | 2015-01-10 |
1 | 552278 | Smith Plumbing | 920000.0 | 1012000.0 | 0.10 | 700.0 | 6 | 15 | 2014 | True | 2014-06-15 |
2 | 23477 | ACME Industrial | 50000.0 | 62500.0 | 0.25 | 125.0 | 3 | 29 | 2016 | True | 2016-03-29 |
3 | 24900 | Brekke LTD | 350000.0 | 490000.0 | 0.04 | 75.0 | 10 | 27 | 2015 | True | 2015-10-27 |
4 | 651029 | Harbor Co | 15000.0 | 12750.0 | -0.15 | NaN | 2 | 2 | 2014 | False | 2014-02-02 |
如今数据已正确转换为咱们须要的全部类型:
df.dtypes
复制代码
Customer Number int64
Customer Name object
2016 float64
2017 float64
Percent Growth float64
Jan Units float64
Month int64
Day int64
Year int64
Active bool
Start_Date datetime64[ns]
复制代码
Dataframe 已准备好进行分析!
在数据采集过程当中应该尽早地使用 astype()
和自定义转换函数。若是你有一个打算重复处理的数据文件,而且它老是以相同的格式存储,你能够定义在读取数据时须要应用的 dtype
和 converters
。将 dtype
视为对数据执行 astype()
颇有帮助。converters
参数容许你将函数应用到各类输入列,相似于上面介绍的方法。
须要注意的是,只能使用 dtype
或 converter
函数中的一种来应用于指定的列。若是你尝试将二者应用于同一列,则会跳过 dtype
。
下面是一个简化的例子,它在数据读入 dataframe 时完成几乎全部的转换:
df_2 = pd.read_csv("sales_data_types.csv",
dtype={'Customer Number': 'int'},
converters={'2016': convert_currency,
'2017': convert_currency,
'Percent Growth': convert_percent,
'Jan Units': lambda x: pd.to_numeric(x, errors='coerce'),
'Active': lambda x: np.where(x == "Y", True, False)
})
df_2.dtypes
复制代码
Customer Number int64
Customer Name object
2016 float64
2017 float64
Percent Growth float64
Jan Units float64
Month int64
Day int64
Year int64
Active object
dtype: object
复制代码
正如前面提到的,我选择了包含用于转换数据的 lambda
示例和函数示例。惟一没法被应用在这里的函数就是那个用来将 Month
、Day
和 Year
三列转换到 datetime
列的函数。不过,这还是一个强大的能够帮助改进数据处理流程的约定。
探索新数据集的第一步是确保数据类型设置正确。大部分时间 Pandas 都会作出合理推论,但数据集中有不少细微差异,所以知道如何使用 Pandas 中的各类数据转换选项很是重要。若是你有任何其余建议,或者有兴趣探索 category
数据类型,请随时在下面发表评论。
掘金翻译计划 是一个翻译优质互联网技术文章的社区,文章来源为 掘金 上的英文分享文章。内容覆盖 Android、iOS、前端、后端、区块链、产品、设计、人工智能等领域,想要查看更多优质译文请持续关注 掘金翻译计划、官方微博、知乎专栏。