呆鸟云:“这一系列长篇终于连载完了,还请大家关注 Python 大咖谈,这里专注 Python 数据分析,后期呆鸟还会给大家分享更多 Pandas 好文。”
数据类型
大多数情况下,pandas 使用 Numpy 数组、Series 或 DataFrame 里某列的数据类型。Numpy 支持 float
、int
、bool
、timedelta[ns]
、datetime64[ns]
,注意,Numpy 不支持带时区信息的 datetime
。
Pandas 与第三方支持库对 Numpy 类型系统进行了扩充,本节只介绍 pandas 的内部扩展。如需了解自行编写与 pandas 配合的扩展类型,请参阅扩展类型,参阅扩展数据类型了解第三方支持库提供的扩展类型。
下表列出了 pandas 扩展类型,参阅列出的文档内容,查看每种类型的详情。
数据种类 | 数据类型 | 标量 | 数组 | 文档 |
---|---|---|---|---|
带时区的日期时间 | DatetimeTZDtype |
Timestamp |
arrays.DatetimeArray |
Time zone handling |
类别型 | CategoricalDtype |
(无) | Categorical |
Categorical data |
时间段 | PeriodDtype |
Period |
arrays.PeriodArray |
Time span representation |
稀疏型 | SparseDtype |
(无) | arrays.SparseArray |
Sparse data structures |
时间间隔 | IntervalDtype |
Interval |
arrays.IntervalArray |
IntervalIndex |
空整型 | Int64Dtype , … |
(无) | arrays.IntegerArray |
Nullable integer data type |
Pandas 用 object
存储字符串。
虽然,object
数据类型能够存储任何对象,但应尽量避免这种操作,要了解与其它支持库与方法的性能与交互操作,参阅 对象转换。
DataFrame 的 dtypes
属性用起来很方便,以 Series 形式返回每列的数据类型。
In [328]: dft = pd.DataFrame({'A': np.random.rand(3),
.....: 'B': 1,
.....: 'C': 'foo',
.....: 'D': pd.Timestamp('20010102'),
.....: 'E': pd.Series([1.0] * 3).astype('float32'),
.....: 'F': False,
.....: 'G': pd.Series([1] * 3, dtype='int8')})
.....:
In [329]: dft
Out[329]:
A B C D E F G
0 0.035962 1 foo 2001-01-02 1.0 False 1
1 0.701379 1 foo 2001-01-02 1.0 False 1
2 0.281885 1 foo 2001-01-02 1.0 False 1
In [330]: dft.dtypes
Out[330]:
A float64
B int64
C object
D datetime64[ns]
E float32
F bool
G int8
dtype: object
要查看 Series
的数据类型,用 dtype
属性。
In [331]: dft['A'].dtype
Out[331]: dtype('float64')
Pandas 对象单列中含多种类型的数据时,该列的数据类型为可适配于各类数据的数据类型,通常为 object
。
# 整数被强制转换为浮点数
In [332]: pd.Series([1, 2, 3, 4, 5, 6.])
Out[332]:
0 1.0
1 2.0
2 3.0
3 4.0
4 5.0
5 6.0
dtype: float64
# 字符串数据决定了该 Series 的数据类型为 ``object``
In [333]: pd.Series([1, 2, 3, 6., 'foo'])
Out[333]:
0 1
1 2
2 3
3 6
4 foo
dtype: object
DataFrame.dtypes.value_counts()
用于统计 DataFrame 里各列数据类型的数量。
In [334]: dft.dtypes.value_counts()
Out[334]:
float32 1
object 1
bool 1
int8 1
float64 1
datetime64[ns] 1
int64 1
dtype: int64
多种数值型数据类型可以在 DataFrame 里共存。如果只传递一种数据类型,不论是通过 dtype
关键字直接传递,还是通过 ndarray
或 Series
传递,都会保存至 DataFrame 操作。此外,不同数值型数据类型不会合并。示例如下:
In [335]: df1 = pd.DataFrame(np.random.randn(8, 1), columns=['A'], dtype='float32')
In [336]: df1
Out[336]:
A
0 0.224364
1 1.890546
2 0.182879
3 0.787847
4 -0.188449
5 0.667715
6 -0.011736
7 -0.399073
In [337]: df1.dtypes
Out[337]:
A float32
dtype: object
In [338]: df2 = pd.DataFrame({'A': pd.Series(np.random.randn(8), dtype='float16'),
.....: 'B': pd.Series(np.random.randn(8)),
.....: 'C': pd.Series(np.array(np.random.randn(8),
.....: dtype='uint8'))})
.....:
In [339]: df2
Out[339]:
A B C
0 0.823242 0.256090 0
1 1.607422 1.426469 0
2 -0.333740 -0.416203 255
3 -0.063477 1.139976 0
4 -1.014648 -1.193477 0
5 0.678711 0.096706 0
6 -0.040863 -1.956850 1
7 -0.357422 -0.714337 0
In [340]: df2.dtypes
Out[340]:
A float16
B float64
C uint8
dtype: object
默认值
整数的默认类型为 int64
,浮点数的默认类型为 float64
,这里的默认值与系统平台无关,不管是 32 位系统,还是 64 位系统都是一样的。下列代码返回的结果都是 int64
:
In [341]: pd.DataFrame([1, 2], columns=['a']).dtypes
Out[341]:
a int64
dtype: object
In [342]: pd.DataFrame({'a': [1, 2]}).dtypes
Out[342]:
a int64
dtype: object
In [343]: pd.DataFrame({'a': 1}, index=list(range(2))).dtypes
Out[343]:
a int64
dtype: object
注意,Numpy 创建数组时,会根据系统选择类型。下列代码在 32 位系统上将返回 int32
。
In [344]: frame = pd.DataFrame(np.array([1, 2]))
向上转型
与其它类型合并时,要用到向上转型,这里指的是从现有类型转换为另一种类型,如int
变为 float
。
In [345]: df3 = df1.reindex_like(df2).fillna(value=0.0) + df2
In [346]: df3
Out[346]:
A B C
0 1.047606 0.256090 0.0
1 3.497968 1.426469 0.0
2 -0.150862 -0.416203 255.0
3 0.724370 1.139976 0.0
4 -1.203098 -1.193477 0.0
5 1.346426 0.096706 0.0
6 -0.052599 -1.956850 1.0
7 -0.756495 -0.714337 0.0
In [347]: df3.dtypes
Out[347]:
A float32
B float64
C float64
dtype: object
DataFrame.to_numpy()
返回多个数据类型里用的最多的数据类型,这里指的是输出结果的数据类型是适用于所有同质 Numpy 数组的数据类型。这里会强制执行向上转型。
In [348]: df3.to_numpy().dtype
Out[348]: dtype('float64')
astype
astype()
方法显式地把一种数据类型转换为另一种,默认返回的是复制数据,就算数据类型没有改变也会执行复制操作,copy=False
可以改变默认操作模式。此外,如果 astype
无效会触发异常。
向上转型一般都会遵循 numpy 的规则。如果操作中涉及两种不同类型的数据,返回的将是更通用的那种数据类型。
In [349]: df3
Out[349]:
A B C
0 1.047606 0.256090 0.0
1 3.497968 1.426469 0.0
2 -0.150862 -0.416203 255.0
3 0.724370 1.139976 0.0
4 -1.203098 -1.193477 0.0
5 1.346426 0.096706 0.0
6 -0.052599 -1.956850 1.0
7 -0.756495 -0.714337 0.0
In [350]: df3.dtypes
Out[350]:
A float32
B float64
C float64
dtype: object
# 转换数据类型
In [351]: df3.astype('float32').dtypes
Out[351]:
A float32
B float32
C float32
dtype: object
用 astype()
把一列或多列转换为指定类型 。
In [352]: dft = pd.DataFrame({'a': [1, 2, 3], 'b': [4, 5, 6], 'c': [7, 8, 9]})
In [353]: dft[['a', 'b']] = dft[['a', 'b']].astype(np.uint8)
In [354]: dft
Out[354]:
a b c
0 1 4 7
1 2 5 8
2 3 6 9
In [355]: dft.dtypes
Out[355]:
a uint8
b uint8
c int64
dtype: object
0.19.0 版新增。
astype()
通过字典指定哪些列转换为哪些类型。
In [356]: dft1 = pd.DataFrame({'a': [1, 0, 1], 'b': [4, 5, 6], 'c': [7, 8, 9]})
In [357]: dft1 = dft1.astype({'a': np.bool, 'c': np.float64})
In [358]: dft1
Out[358]:
a b c
0 True 4 7.0
1 False 5 8.0
2 True 6 9.0
In [359]: dft1.dtypes
Out[359]:
a bool
b int64
c float64
dtype: object
::: tip 注意
用 astype()
与 loc()
为部分列转换指定类型时,会发生向上转型。
loc()
尝试分配当前的数据类型,而 []
则会从右方获取数据类型并进行覆盖。因此,下列代码会产出意料之外的结果:
In [360]: dft = pd.DataFrame({'a': [1, 2, 3], 'b': [4, 5, 6], 'c': [7, 8, 9]})
In [361]: dft.loc[:, ['a', 'b']].astype(np.uint8).dtypes
Out[361]:
a uint8
b uint8
dtype: object
In [362]: dft.loc[:, ['a', 'b']] = dft.loc[:, ['a', 'b']].astype(np.uint8)
In [363]: dft.dtypes
Out[363]:
a int64
b int64
c int64
dtype: object
:::
对象转换
Pandas 提供了多种函数可以把 object
从一种类型强制转为另一种类型。这是因为,数据有时存储的是正确类型,但在保存时却存成了 object
类型,此时,用 DataFrame.infer_objects()
与 Series.infer_objects()
方法即可把数据软转换为正确的类型。
In [364]: import datetime
In [365]: df = pd.DataFrame([[1, 2],
.....: ['a', 'b'],
.....: [datetime.datetime(2016, 3, 2),
.....: datetime.datetime(2016, 3, 2)]])
.....:
In [366]: df = df.T
In [367]: df
Out[367]:
0 1 2
0 1 a 2016-03-02
1 2 b 2016-03-02
In [368]: df.dtypes
Out[368]:
0 object
1 object
2 datetime64[ns]
dtype: object
因为数据被转置,所以把原始列的数据类型改成了 object
,但使用 infer_objects
后就变正确了。
In [369]: df.infer_objects().dtypes
Out[369]:
0 int64
1 object
2 datetime64[ns]
dtype: object
下列函数可以应用于一维数组与标量,执行硬转换,把对象转换为指定类型。
-
to_numeric()
,转换为数值型
In [370]: m = ['1.1', 2, 3]
In [371]: pd.to_numeric(m)
Out[371]: array([1.1, 2. , 3. ])
-
to_datetime()
,转换为datetime
对象
In [372]: import datetime
In [373]: m = ['2016-07-09', datetime.datetime(2016, 3, 2)]
In [374]: pd.to_datetime(m)
Out[374]: DatetimeIndex(['2016-07-09', '2016-03-02'], dtype='datetime64[ns]', freq=None)
-
to_timedelta()
,转换为timedelta
对象。
In [375]: m = ['5us', pd.Timedelta('1day')]
In [376]: pd.to_timedelta(m)
Out[376]: TimedeltaIndex(['0 days 00:00:00.000005', '1 days 00:00:00'], dtype='timedelta64[ns]', freq=None)
如需强制转换,则要加入 error
参数,指定 pandas 怎样处理不能转换为成预期类型或对象的数据。errors
参数的默认值为 False
,指的是在转换过程中,遇到任何问题都触发错误。设置为 errors='coerce'
时,pandas 会忽略错误,强制把问题数据转换为 pd.NaT
(datetime
与 timedelta
),或 np.nan
(数值型)。读取数据时,如果大部分要转换的数据是数值型或 datetime
,这种操作非常有用,但偶尔也会有非制式数据混合在一起,可能会导致展示数据缺失:
In [377]: import datetime
In [378]: m = ['apple', datetime.datetime(2016, 3, 2)]
In [379]: pd.to_datetime(m, errors='coerce')
Out[379]: DatetimeIndex(['NaT', '2016-03-02'], dtype='datetime64[ns]', freq=None)
In [380]: m = ['apple', 2, 3]
In [381]: pd.to_numeric(m, errors='coerce')
Out[381]: array([nan, 2., 3.])
In [382]: m = ['apple', pd.Timedelta('1day')]
In [383]: pd.to_timedelta(m, errors='coerce')
Out[383]: TimedeltaIndex([NaT, '1 days'], dtype='timedelta64[ns]', freq=None)
error
参数还有第三个选项,error='ignore'
。转换数据时会忽略错误,直接输出问题数据:
In [384]: import datetime
In [385]: m = ['apple', datetime.datetime(2016, 3, 2)]
In [386]: pd.to_datetime(m, errors='ignore')
Out[386]: Index(['apple', 2016-03-02 00:00:00], dtype='object')
In [387]: m = ['apple', 2, 3]
In [388]: pd.to_numeric(m, errors='ignore')
Out[388]: array(['apple', 2, 3], dtype=object)
In [389]: m = ['apple', pd.Timedelta('1day')]
In [390]: pd.to_timedelta(m, errors='ignore')
Out[390]: array(['apple', Timedelta('1 days 00:00:00')], dtype=object)
执行转换操作时,to_numeric()
还有一个参数,downcast
,即向下转型,可以把数值型转换为减少内存占用的数据类型:
In [391]: m = ['1', 2, 3]
In [392]: pd.to_numeric(m, downcast='integer') # smallest signed int dtype
Out[392]: array([1, 2, 3], dtype=int8)
In [393]: pd.to_numeric(m, downcast='signed') # same as 'integer'
Out[393]: array([1, 2, 3], dtype=int8)
In [394]: pd.to_numeric(m, downcast='unsigned') # smallest unsigned int dtype
Out[394]: array([1, 2, 3], dtype=uint8)
In [395]: pd.to_numeric(m, downcast='float') # smallest float dtype
Out[395]: array([1., 2., 3.], dtype=float32)
上述方法仅能应用于一维数组、列表或标量;不能直接用于 DataFrame 等多维对象。不过,用 apply()
,可以快速为每列应用函数:
In [396]: import datetime
In [397]: df = pd.DataFrame([
.....: ['2016-07-09', datetime.datetime(2016, 3, 2)]] * 2, dtype='O')
.....:
In [398]: df
Out[398]:
0 1
0 2016-07-09 2016-03-02 00:00:00
1 2016-07-09 2016-03-02 00:00:00
In [399]: df.apply(pd.to_datetime)
Out[399]:
0 1
0 2016-07-09 2016-03-02
1 2016-07-09 2016-03-02
In [400]: df = pd.DataFrame([['1.1', 2, 3]] * 2, dtype='O')
In [401]: df
Out[401]:
0 1 2
0 1.1 2 3
1 1.1 2 3
In [402]: df.apply(pd.to_numeric)
Out[402]:
0 1 2
0 1.1 2 3
1 1.1 2 3
In [403]: df = pd.DataFrame([['5us', pd.Timedelta('1day')]] * 2, dtype='O')
In [404]: df
Out[404]:
0 1
0 5us 1 days 00:00:00
1 5us 1 days 00:00:00
In [405]: df.apply(pd.to_timedelta)
Out[405]:
0 1
0 00:00:00.000005 1 days
1 00:00:00.000005 1 days
各种坑
对 integer
数据执行选择操作时,可以很轻而易举地把数据转换为 floating
。pandas 会保存输入数据的数据类型,以防未引入 nans
的情况。参阅 对整数 NA 空值的支持。
In [406]: dfi = df3.astype('int32')
In [407]: dfi['E'] = 1
In [408]: dfi
Out[408]:
A B C E
0 1 0 0 1
1 3 1 0 1
2 0 0 255 1
3 0 1 0 1
4 -1 -1 0 1
5 1 0 0 1
6 0 -1 1 1
7 0 0 0 1
In [409]: dfi.dtypes
Out[409]:
A int32
B int32
C int32
E int64
dtype: object
In [410]: casted = dfi[dfi > 0]
In [411]: casted
Out[411]:
A B C E
0 1.0 NaN NaN 1
1 3.0 1.0 NaN 1
2 NaN NaN 255.0 1
3 NaN 1.0 NaN 1
4 NaN NaN NaN 1
5 1.0 NaN NaN 1
6 NaN NaN 1.0 1
7 NaN NaN NaN 1
In [412]: casted.dtypes
Out[412]:
A float64
B float64
C float64
E int64
dtype: object
浮点数类型未改变。
In [413]: dfa = df3.copy()
In [414]: dfa['A'] = dfa['A'].astype('float32')
In [415]: dfa.dtypes
Out[415]:
A float32
B float64
C float64
dtype: object
In [416]: casted = dfa[df2 > 0]
In [417]: casted
Out[417]:
A B C
0 1.047606 0.256090 NaN
1 3.497968 1.426469 NaN
2 NaN NaN 255.0
3 NaN 1.139976 NaN
4 NaN NaN NaN
5 1.346426 0.096706 NaN
6 NaN NaN 1.0
7 NaN NaN NaN
In [418]: casted.dtypes
Out[418]:
A float32
B float64
C float64
dtype: object
基于 dtype
选择列
select_dtypes()
方法基于 dtype
选择列。
首先,创建一个由多种数据类型组成的 DataFrame:
In [419]: df = pd.DataFrame({'string': list('abc'),
.....: 'int64': list(range(1, 4)),
.....: 'uint8': np.arange(3, 6).astype('u1'),
.....: 'float64': np.arange(4.0, 7.0),
.....: 'bool1': [True, False, True],
.....: 'bool2': [False, True, False],
.....: 'dates': pd.date_range('now', periods=3),
.....: 'category': pd.Series(list("ABC")).astype('category')})
.....:
In [420]: df['tdeltas'] = df.dates.diff()
In [421]: df['uint64'] = np.arange(3, 6).astype('u8')
In [422]: df['other_dates'] = pd.date_range('20130101', periods=3)
In [423]: df['tz_aware_dates'] = pd.date_range('20130101', periods=3, tz='US/Eastern')
In [424]: df
Out[424]:
string int64 uint8 float64 bool1 bool2 dates category tdeltas uint64 other_dates tz_aware_dates
0 a 1 3 4.0 True False 2019-08-22 15:49:01.870038 A NaT 3 2013-01-01 2013-01-01 00:00:00-05:00
1 b 2 4 5.0 False True 2019-08-23 15:49:01.870038 B 1 days 4 2013-01-02 2013-01-02 00:00:00-05:00
2 c 3 5 6.0 True False 2019-08-24 15:49:01.870038 C 1 days 5 2013-01-03 2013-01-03 00:00:00-05:00
该 DataFrame 的数据类型:
In [425]: df.dtypes
Out[425]:
string object
int64 int64
uint8 uint8
float64 float64
bool1 bool
bool2 bool
dates datetime64[ns]
category category
tdeltas timedelta64[ns]
uint64 uint64
other_dates datetime64[ns]
tz_aware_dates datetime64[ns, US/Eastern]
dtype: object
select_dtypes()
有两个参数,include
与 exclude
,用于实现“提取这些数据类型的列” (include
)或 “提取不是这些数据类型的列”(exclude
)。
选择 bool
型的列,示例如下:
In [426]: df.select_dtypes(include=[bool])
Out[426]:
bool1 bool2
0 True False
1 False True
2 True False
该方法还支持输入 NumPy 数据类型的名称:
In [427]: df.select_dtypes(include=['bool'])
Out[427]:
bool1 bool2
0 True False
1 False True
2 True False
select_dtypes()
还支持通用数据类型。
比如,选择所有数值型与布尔型的列,同时,排除无符号整数:
In [428]: df.select_dtypes(include=['number', 'bool'], exclude=['unsignedinteger'])
Out[428]:
int64 float64 bool1 bool2 tdeltas
0 1 4.0 True False NaT
1 2 5.0 False True 1 days
2 3 6.0 True False 1 days
选择字符串型的列必须要用 object
:
In [429]: df.select_dtypes(include=['object'])
Out[429]:
string
0 a
1 b
2 c
要查看 numpy.number
等通用 dtype
的所有子类型,可以定义一个函数,返回子类型树:
In [430]: def subdtypes(dtype):
.....: subs = dtype.__subclasses__()
.....: if not subs:
.....: return dtype
.....: return [dtype, [subdtypes(dt) for dt in subs]]
.....:
所有 Numpy 数据类型都是 numpy.generic
的子类:
In [431]: subdtypes(np.generic)
Out[431]:
[numpy.generic,
[[numpy.number,
[[numpy.integer,
[[numpy.signedinteger,
[numpy.int8,
numpy.int16,
numpy.int32,
numpy.int64,
numpy.int64,
numpy.timedelta64]],
[numpy.unsignedinteger,
[numpy.uint8,
numpy.uint16,
numpy.uint32,
numpy.uint64,
numpy.uint64]]]],
[numpy.inexact,
[[numpy.floating,
[numpy.float16, numpy.float32, numpy.float64, numpy.float128]],
[numpy.complexfloating,
[numpy.complex64, numpy.complex128, numpy.complex256]]]]]],
[numpy.flexible,
[[numpy.character, [numpy.bytes_, numpy.str_]],
[numpy.void, [numpy.record]]]],
numpy.bool_,
numpy.datetime64,
numpy.object_]]
::: tip 注意
Pandas 支持 category
与 datetime64[ns, tz]
类型,但这两种类型未整合到 Numpy 的架构里,因此,上面的函数没有显示。
:::