数据分析篇 | Pandas基础用法6【完结篇】
- 2019 年 12 月 11 日
- 筆記
以下文章来源于Python大咖谈,作者吱吱不倦的呆鸟
数据类型
大多数情况下,pandas 使用 Numpy 数组、Series 或 DataFrame 里某列的数据类型。Numpy 支持 float
、int
、bool
、timedelta[ns]
、datetime64[ns]
,注意,Numpy 不支持带时区信息的 datetime
。
Pandas 与第三方支持库对 Numpy 类型系统进行了扩充,本节只介绍 pandas 的内部扩展。如需了解自行编写与 pandas 配合的扩展类型,请参阅扩展类型,参阅扩展数据类型了解第三方支持库提供的扩展类型。
下表列出了 pandas 扩展类型,参阅列出的文档内容,查看每种类型的详情。
数据种类 |
数据类型 |
标量 |
数组 |
文档 |
---|---|---|---|---|
带时区的日期时间 |
DatetimeTZ |
Timestamp |
arrays.DatetimeArray |
Time zone handling |
类别型 |
Categorical |
(无) |
Categorical |
Categorical data |
时间段 |
Period |
Period |
arrays.PeriodArray |
Time span representation |
稀疏数据 |
Sparse |
(无) |
arrays.SparseArray |
Sparse data structures |
时间间隔 |
Interval |
Interval |
arrays.IntervalArray |
IntervalIndex |
空整型 |
Int64… |
(无) |
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
用
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_]]
注意:Pandas 支持
category
与datetime64[ns, tz]
类型,但这两种类型未整合到 Numpy 的架构里,因此,上面的函数没有显示。
【完】