问题如题。一般pandas读取数据的时候,会自动检测数据的类型,但有时候可能不是特别准确,还需要我们自己做类型转换。比如下面这种:
a = [['a', '1.2', '4.2'], ['b', '70', '0.03'], ['x', '5', '0']]
df = pd.DataFrame(a)
df.dtypes
# 0 object
# 1 object
# 2 object
# dtype: object
如何修改第2、3列的类型?扩展一下,如果有很多列的时候,如何高效的修改?
Pandas中主要有4种类型转换相关的方法:
to_numeric/to_datetime/to_timedelta
:可以参数转换为合适的对应类型。astype
infer_objects
convert_dtypes
to_xxx
to_numeric
to_numeric
将参数转换为合适的数值类型(float64/int64)。签名如下:
pandas.to_numeric(arg, errors='raise', downcast=None)
先看一些使用例子:
In [2]: s = pd.Series(["8", 6, "7.5", 3, "0.9"])
In [3]: s
Out[3]:
0 8
1 6
2 7.5
3 3
4 0.9
dtype: object
In [4]: pd.to_numeric(s)
Out[4]:
0 8.0
1 6.0
2 7.5
3 3.0
4 0.9
dtype: float64
可以使用apply()
方法批量转换DataFrame里面的列:
In [11]: df = pd.DataFrame([['1', '2', '3'],['4', '5', '6'],['7.1', '8.0', '9']], columns=['a','b', 'c'])
In [12]: df
Out[12]:
a b c
0 1 2 3
1 4 5 6
2 7.1 8.0 9
In [13]: df.dtypes
Out[13]:
a object
b object
c object
dtype: object
In [14]: df_1=df.apply(pd.to_numeric)
In [15]: df_1
Out[15]:
a b c
0 1.0 2.0 3
1 4.0 5.0 6
2 7.1 8.0 9
In [16]: df_1.dtypes
Out[16]:
a float64
b float64
c int64
dtype: object
也可以只对某些列进行转换:
In [18]: df[['a','b']]=df[['a','b']].apply(pd.to_numeric)
In [19]: df
Out[19]:
a b c
0 1.0 2.0 3
1 4.0 5.0 6
2 7.1 8.0 9
In [20]: df.dtypes
Out[20]:
a float64
b float64
c object
dtype: object
类型转换难免会产生错误,比如无法转换等,to_numeric
提供了一个参数errors
来让用户控制发生错误时如何处理,用有三个选项:
- 'raise':默认值,即抛出异常
- 'ignore':转换失败时,保留原值
- 'coerce':转换失败时,设置为NaN
看一些例子:
In [21]: df=pd.DataFrame([['1','2'],['3','4'],['5','s']], columns=['a','b'])
In [22]: df
Out[22]:
a b
0 1 2
1 3 4
2 5 s
In [23]: df.dtypes
Out[23]:
a object
b object
dtype: object
In [24]: df.apply(pd.to_numeric)
ValueError: Unable to parse string "s" at position 2
In [25]: df.apply(pd.to_numeric, errors='coerce')
Out[25]:
a b
0 1 2.0
1 3 4.0
2 5 NaN
In [26]: df.apply(pd.to_numeric, errors='ignore')
Out[26]:
a b
0 1 2
1 3 4
2 5 s
to_numeric
默认会转换为float64或者int64,如果你想节省内存转换为小一些的类型的话,可以使用to_numeric提供的downcast
参数,可选值如下:
- 'integer'或者'signed':转换为np.int8
- 'unsigned':转换为np.uint8
- 'float':转换为np.float32
看一些例子:
In [29]: s = pd.Series(['1','2','-7'])
In [30]: s
Out[30]:
0 1
1 2
2 -7
dtype: object
In [31]: pd.to_numeric(s)
Out[31]:
0 1
1 2
2 -7
dtype: int64
In [32]: pd.to_numeric(s, downcast='integer')
Out[32]:
0 1
1 2
2 -7
dtype: int8
# 注意这里:因为 unsigned无法表示-7,所以这里实际没有发生downcast
In [33]: pd.to_numeric(s, downcast='unsigned')
Out[33]:
0 1
1 2
2 -7
dtype: int64
In [34]: pd.to_numeric(s, downcast='float')
Out[34]:
0 1.0
1 2.0
2 -7.0
dtype: float32
这里有2个注意点:
- downcast是发生在核心的类型转换之后的(也就是先将原始类型转换为float64/int64,然后再执行downcast动作),所以前面介绍的那个
errors
参数对downcast这里是无效的。 如果目标类型无法容纳被转换的值,就不会发生实际的转换。比如上面尝试转换为'unsigned'类型时,因为-7无法转换为unsigned,所以实际没有执行downcast。
to_datetime
to_datetime
把参数转换为datetime类型,相比于to_numeric
,函数原型复杂了一些。pandas.to_datetime(arg, errors='raise', dayfirst=False, yearfirst=False, utc=None, format=None, exact=True, unit=None, infer_datetime_format=False, origin='unix', cache=True)
看一些使用例子:
# 可以使用这些关键字来构造表示时间日期的字典:[‘year’, ‘month’, ‘day’, ‘minute’, ‘second’, ‘ms’, ‘us’, ‘ns’]),复数也可以 In [35]: df = pd.DataFrame({'year': [2015, 2016], 'month': [2, 3], 'day':[4, 5]}) In [36]: df Out[36]:
- 2015 2 4
2016 3 5
In [37]: df.dtypes
Out[37]:
year int64
month int64
day int64
dtype: objectIn [38]: pd.to_datetime(df)
Out[38]:- 2015-02-04
2016-03-05
dtype: datetime64[ns]In [40]: pd.to_datetime(1490195805, unit='s')
Out[40]: Timestamp('2017-03-22 15:16:45')In [41]: pd.to_datetime(1490195805433502912, unit='ns')
Out[41]: Timestamp('2017-03-22 15:16:45.433502912')In [42]: pd.to_datetime("10/11/12", dayfirst=True)
Out[42]: Timestamp('2012-11-10 00:00:00')In [43]: pd.to_datetime("10/11/12", yearfirst=True)
Out[43]: Timestamp('2010-11-12 00:00:00')In [44]: pd.to_datetime("10/11/12", dayfirst=True, yearfirst=True)
Out[44]: Timestamp('2010-12-11 00:00:00')`errors`字段含义同`to_numeric`:
- 'raise':默认值,即抛出异常
- 'ignore':转换失败时,保留原值
- 'coerce':转换失败时,设置为NaT。这里注意一种情况就是Pandas的时间精度是纳秒,且用int64表示,大概只能表示584年这样一个范围([pd.Timestamp.min, pd.Timestamp.max]),当转换的时间超出这个范围时也算失败。
看个例子:
In [46]: pd.Timestamp.min
Out[46]: Timestamp('1677-09-21 00:12:43.145225')
In [47]: pd.Timestamp.max
Out[47]: Timestamp('2262-04-11 23:47:16.854775807')
In [48]: pd.to_datetime('13000101', format='%Y%m%d', errors='raise')
OutOfBoundsDatetime: Out of bounds nanosecond timestamp: 1300-01-01 00:00:00
In [49]: pd.to_datetime('13000101', format='%Y%m%d', errors='ignore')
Out[49]: datetime.datetime(1300, 1, 1, 0, 0)
In [50]: pd.to_datetime('13000101', format='%Y%m%d', errors='coerce')
Out[50]: NaT
In [53]: pd.to_datetime('130000101', format='%Y%m%d', errors='ignore')
Out[53]: '130000101'
to_timedelta
timedelta类型表示两个时间的绝对差值,to_timedelta
将参数转换为timedelta类型。方法签名如下:
pandas.to_timedelta(arg, unit=None, errors='raise')
unit
用于指定参数的单位,默认为ns
,合法的取值如下:
- ‘W’
- ‘D’ / ‘days’ / ‘day’
- ‘hours’ / ‘hour’ / ‘hr’ / ‘h’
- ‘m’ / ‘minute’ / ‘min’ / ‘minutes’ / ‘T’
- ‘S’ / ‘seconds’ / ‘sec’ / ‘second’
- ‘ms’ / ‘milliseconds’ / ‘millisecond’ / ‘milli’ / ‘millis’ / ‘L’
- ‘us’ / ‘microseconds’ / ‘microsecond’ / ‘micro’ / ‘micros’ / ‘U’
- ‘ns’ / ‘nanoseconds’ / ‘nano’ / ‘nanos’ / ‘nanosecond’ / ‘N’
errors
的取值同to_datetime
。看几个使用例子:
In [55]: pd.to_timedelta('1 days 06:05:01.00003')
Out[55]: Timedelta('1 days 06:05:01.000030')
In [56]: pd.to_timedelta('15.5us')
Out[56]: Timedelta('0 days 00:00:00.000015500')
In [57]: pd.to_timedelta(['1 days 06:05:01.00003', '15.5us', 'nan'])
Out[57]: TimedeltaIndex(['1 days 06:05:01.000030', '0 days 00:00:00.000015500', NaT], dtype='timedelta64[ns]', freq=None)
In [58]: import numpy as np
In [59]: pd.to_timedelta(np.arange(5), unit='s')
Out[59]:
TimedeltaIndex(['0 days 00:00:00', '0 days 00:00:01', '0 days 00:00:02',
'0 days 00:00:03', '0 days 00:00:04'],
dtype='timedelta64[ns]', freq=None)
In [60]: pd.to_timedelta(np.arange(5), unit='d')
Out[60]: TimedeltaIndex(['0 days', '1 days', '2 days', '3 days', '4 days'], dtype='timedelta64[ns]', freq=None)
astype
astype方法可以做任意类型的转换(当然未必能成功)。方法原型如下:
pd.DataFrame.astype(dtype, copy=True, errors='raise')
dtype
就是我们想要转换成的目标类型,可以使用Numpy的类型、Python的部分类型、Pandas特有的类型。copy
表示是否修改原数据。errors
可以取'raise'(失败是抛异常)或者'ignore'(失败时忽略并返回原值)。
看一些例子:
In [62]: df = pd.DataFrame({'col1': [1, 2], 'col2': [3, 4]})
In [63]: df.dtypes
Out[63]:
col1 int64
col2 int64
dtype: object
In [64]: df.astype('int32').dtypes
Out[64]:
col1 int32
col2 int32
dtype: object
In [65]: s = pd.Series([1, 3], dtype='int32')
In [66]: s
Out[66]:
0 1
1 3
dtype: int32
In [67]: s.astype('int64')
Out[67]:
0 1
1 3
dtype: int64
In [68]: s.astype('category')
Out[68]:
0 1
1 3
dtype: category
Categories (2, int64): [1, 3]
In [77]: s_date = pd.Series(['20220101', '20220102', '20220103'])
In [78]: s_date
Out[78]:
0 20220101
1 20220102
2 20220103
dtype: object
In [79]: s_date.astype('datetime64')
Out[79]:
0 2022-01-01
1 2022-01-02
2 2022-01-03
dtype: datetime64[ns]
In [81]: i = pd.Series([1,2,3])
In [82]: i
Out[82]:
0 1
1 2
2 3
dtype: int64
In [83]: i.astype(str)
Out[83]:
0 1
1 2
2 3
dtype: object
In [84]: s = pd.Series([1,2,-7])
In [85]: s.astype(np.int8)
Out[85]:
0 1
1 2
2 -7
dtype: int8
In [86]: s.astype(np.uint8)
Out[86]:
0 1
1 2
2 249
dtype: uint8
使用astype的时候,要注意范围,比如下面的转换不会报错,但不是我们想要的:
In [95]: s = pd.Series([1,2,-7])
# -7被转换为249
In [96]: s.astype(np.uint8)
Out[96]:
0 1
1 2
2 249
dtype: uint8
# 使用to_numeric就不会有问题
In [97]: pd.to_numeric(s, downcast='unsigned')
Out[97]:
0 1
1 2
2 -7
dtype: int64
infer_objects
pandas 0.21.0版本加入,会尝试推测类型。看个例子:
In [103]: df = pd.DataFrame({'a': [7, 1, 5], 'b': ['3','2','1']}, dtype='object')
In [104]: df.dtypes
Out[104]:
a object
b object
dtype: object
In [105]: df.infer_objects().dtypes
Out[105]:
a int64
b object
dtype: object
可以看到,infer_objects
其实还是比较“肤浅”的,如果要将'b'列也转换成数值型,可以使用前面介绍的方法。
convert_dtypes
convert_dtypes
会尝试将各列转换为最可能的类型,其特点是支持pd.NA
,方法签名如下:
DataFrame.convert_dtypes(infer_objects=True, convert_string=True, convert_integer=True, convert_boolean=True, convert_floating=True)
通过各个类型参数可以控制某些类型是否转换;infer_objects
为True时会尝试转换object类型为更具体的类型。看一些例子:
In [110]: df = pd.DataFrame(
...: {
...: 'a': pd.Series([1, 2, 3], dtype=np.dtype('int32')),
...: 'b': pd.Series(['x', 'y', 'z'], dtype=np.dtype('O')),
...: 'c': pd.Series([True, False, np.nan], dtype=np.dtype('O')),
...: 'd': pd.Series(['h', 'i', np.nan], dtype=np.dtype('O')),
...: 'e': pd.Series([10, np.nan, 20], dtype=np.dtype('float')),
...: 'f': pd.Series([np.nan, 100.5, 200], dtype=np.dtype('float')),
...: }
...: )
In [111]: df
Out[111]:
a b c d e f
0 1 x True h 10.0 NaN
1 2 y False i NaN 100.5
2 3 z NaN NaN 20.0 200.0
In [112]: df.dtypes
Out[112]:
a int32
b object
c object
d object
e float64
f float64
dtype: object
In [113]: dfn = df.convert_dtypes()
In [114]: dfn
Out[114]:
a b c d e f
0 1 x True h 10 NaN
1 2 y False i <NA> 100.5
2 3 z <NA> <NA> 20 200.0
In [115]: dfn.dtypes
Out[115]:
a Int32
b string
c boolean
d string
e Int64
f float64
dtype: object
End, that's all!
评论已关闭