pandas数据类型
大多数情况下,pandas在Series和DataFrame的columns中使用numpy 的 arrays 和 dtypes 。
numpy提供对float
,int
,bool
,timedelta64[ns]
,datetime64[ns]
数据类型的支持。
但是numpy不支持timezone-aware datetimes
pandas和一些第三方库在一些地方扩展了numpy的类型系统。
pandas扩展了以下数据类型:
Kind of Data | Data Type | Scalar | Array | Documentation |
---|---|---|---|---|
tz-aware datetime | DatetimeTZDtype | Timestamp | arrays.DatetimeArray | Time zone handling |
Categorical | CategoricalDtype | (none) | Categorical | Categorical data |
period (time spans) | PeriodDtype | Period | arrays.PeriodArray | Time span representation |
sparse | SparseDtype | (none) | arrays.SparseArray | Sparse data structures |
intervals | IntervalDtype | Interval | arrays.IntervalArray | IntervalIndex |
nullable integer | Int64Dtype, … | (none) | arrays.IntegerArray | Nullable integer data type |
panda使用对象dtype存储字符串。
最后,可以使用对象dtype存储任意对象,但应尽可能避免使用(有关性能以及与其他库和方法的互操作性)。
DataFrame的一个方便的dtypes
属性,设置Series column的属性。
import numpy as np
import pandas as pd
dft = pd.DataFrame({
'A':np.random.rand(3),
'B':1,
'C':'foo',
'D':pd.Timestamp('20010102'),
'E':pd.Series([1.]*3).astype('float32'),
'F':False,
'G':pd.Series([1]*3,dtype='int8')
})
dft
A | B | C | D | E | F | G | |
---|---|---|---|---|---|---|---|
0 | 0.102392 | 1 | foo | 2001-01-02 | 1.0 | False | 1 |
1 | 0.597735 | 1 | foo | 2001-01-02 | 1.0 | False | 1 |
2 | 0.520656 | 1 | foo | 2001-01-02 | 1.0 | False | 1 |
dft.dtypes
A float64
B int64
C object
D datetime64[ns]
E float32
F bool
G int8
dtype: object
dft.A.dtype
dtype('float64')
如果pandas对象在单个列中包含具有多个dtypes的数据,则将选择一个能容纳所有数据类型的dtype作为该列的dtype(object是最通用的)。
pd.Series([1,2,3,4,5,6.])
0 1.0
1 2.0
2 3.0
3 4.0
4 5.0
5 6.0
dtype: float64
pd.Series([1,2,3,6.,'foo'])
0 1
1 2
2 3
3 6
4 foo
dtype: object
可以通过调用以下内容找到DataFrame中每种类型的列数.DataFrame.dtypes.value_counts().
dft.dtypes.value_counts()
datetime64[ns] 1
float32 1
bool 1
object 1
int64 1
int8 1
float64 1
dtype: int64
数值类型将会传播并共存于DataFrame中。如果一个dtype被传入(不管是通过dtype
关键字,传入的ndarray
还是Series
),她会被保存在DataFrame中。而且不同类型的numeric dtypes 不会被组合在一起。
以下是一个示例:
df1 = pd.DataFrame(np.random.rand(8,1),columns=['A'],dtype='float32')
df1
A | |
---|---|
0 | 0.889056 |
1 | 0.108117 |
2 | 0.494659 |
3 | 0.308186 |
4 | 0.217554 |
5 | 0.485128 |
6 | 0.344791 |
7 | 0.161693 |
df1.dtypes
A float32
dtype: object
df2 = pd.DataFrame({
'A':pd.Series(np.random.randn(8),dtype='float16'),
'B':pd.Series(np.random.rand(8)),
'C':pd.Series(np.array(np.random.randn(8),dtype='uint8'))
})
df2
A | B | C | |
---|---|---|---|
0 | -1.427734 | 0.158445 | 0 |
1 | -0.291748 | 0.315184 | 0 |
2 | -0.755859 | 0.395976 | 255 |
3 | -1.899414 | 0.115210 | 1 |
4 | -0.565430 | 0.962711 | 0 |
5 | -1.768555 | 0.004223 | 0 |
6 | -1.620117 | 0.176138 | 255 |
7 | -0.805176 | 0.594955 | 0 |
df2.dtypes
A float16
B float64
C uint8
dtype: object
默认值
默认情况下,int
值保存为int64
,float
值保存为float64
。
pandas不会根据操作系统来选择使用int64还是int32,但是numpy在创建arrays的时候,会根据操作系统选择使用int32还是int64。
向上转型
与其他类型结合使用时,可能会转换类型,这意味着它们会从当前类型向上转型(例如,从int转换为float)。
df3 = df1.reindex_like(df2).fillna(value=0.0) + df2
df3
A | B | C | |
---|---|---|---|
0 | -0.538678 | 0.158445 | 0.0 |
1 | -0.183631 | 0.315184 | 0.0 |
2 | -0.261200 | 0.395976 | 255.0 |
3 | -1.591228 | 0.115210 | 1.0 |
4 | -0.347876 | 0.962711 | 0.0 |
5 | -1.283427 | 0.004223 | 0.0 |
6 | -1.275327 | 0.176138 | 255.0 |
7 | -0.643483 | 0.594955 | 0.0 |
df3.dtypes
A float32
B float64
C float64
dtype: object
DataFrame.to_numpy()
会返回的numpy对象可能会强制数量类型向上转型。
df3.to_numpy().dtype
dtype('float64')
astype
可以使用astype()
方法可以容易的将dtype转变为另一种。它将默认返回一个复制,即使dtype没有改变(传入参数copy=False
可以默认行为)。如果astype操作不合法,将抛出异常。
向上转型是根据numpy
的规则。如果两个不同的dtype在一次操作中被涉及,那么更通用的那个将会被使用。
df3
A | B | C | |
---|---|---|---|
0 | -0.538678 | 0.158445 | 0.0 |
1 | -0.183631 | 0.315184 | 0.0 |
2 | -0.261200 | 0.395976 | 255.0 |
3 | -1.591228 | 0.115210 | 1.0 |
4 | -0.347876 | 0.962711 | 0.0 |
5 | -1.283427 | 0.004223 | 0.0 |
6 | -1.275327 | 0.176138 | 255.0 |
7 | -0.643483 | 0.594955 | 0.0 |
df3.dtypes
A float32
B float64
C float64
dtype: object
df3.astype('float32').dtypes
A float32
B float32
C float32
dtype: object
将DataFrame中的某些Series转换为一个特殊的类型
dft = pd.DataFrame({'a': [1, 2, 3], 'b': [4, 5, 6], 'c': [7, 8, 9]})
dft.dtypes
a int64
b int64
c int64
dtype: object
dft[['a', 'b']] = dft[['a', 'b']].astype(np.uint8)
dft.dtypes
a uint8
b uint8
c int64
dtype: object
当尝试使用astype()和loc()将列的子集转换为指定类型时,会发生向上转换。
下面这段代码会产生意想不到的结果。
dft = pd.DataFrame({'a': [1, 2, 3], 'b': [4, 5, 6], 'c': [7, 8, 9]})
dft.loc[:, ['a', 'b']].astype(np.uint8).dtypes
a uint8
b uint8
dtype: object
dft.loc[:, ['a', 'b']] = dft.loc[:, ['a', 'b']].astype(np.uint8)
dft.dtypes
a int64
b int64
c int64
dtype: object
object转换
panda提供了各种函数来尝试强制类型从对象dtype转换为其他类型。如果数据已经是正确的类型,但存储在对象数组中,则可以使用datafram .infer_objects()
和seri_infer_objects()
方法来软转换为正确的类型。
import datetime
df =pd.DataFrame([
[1,2],['a','b'],[datetime.datetime(2016,3,2),datetime.datetime(2016,3,2)]
])
df
0 | 1 | |
---|---|---|
0 | 1 | 2 |
1 | a | b |
2 | 2016-03-02 00:00:00 | 2016-03-02 00:00:00 |
df = df.T
df.dtypes
0 object
1 object
2 datetime64[ns]
dtype: object
因为数据被转置,原始数据存储的是object,所以上面的df.dtypes输出的是object。
使用infer_object()
可以修正dtypes:
df.infer_objects().dtypes
0 int64
1 object
2 datetime64[ns]
dtype: object
以下方法可以将一维对象数组或者标量转化为其他类型:
to_numeric()
:转变成numeric dtypesto_datetime()
:转变为datetime objectsto_timedelta()
:转变为timedelta objects
m = ['5us', pd.Timedelta('1day')]
m
['5us', Timedelta('1 days 00:00:00')]
pd.to_timedelta(m)
TimedeltaIndex(['0 days 00:00:00.000005', '1 days 00:00:00'], dtype='timedelta64[ns]', freq=None)
为了强制转换类型,可以传入一个errors
参数,它将用指定怎么处理哪些不能没转化为指定类型的元素。
默认errors=raise
,意味着在转化过程中遇到错误都会抛出。如果errors=coerce
,错误将会被忽略并且将出错的元素转化为pd.NaT
或者pd.NaN
类型。
当读取数据,这些数据大部分是符合要求的类型,但是有一些不符合的元素混在其中,希望把不符合的设置为缺失,上面说讲的特性将非常有用。
import datetime
m = ['apple', datetime.datetime(2016, 3, 2)]
pd.to_datetime(m, errors='coerce')
DatetimeIndex(['NaT', '2016-03-02'], dtype='datetime64[ns]', freq=None)
m = ['apple', 2, 3]
pd.to_numeric(m, errors='coerce')
array([nan, 2., 3.])
m = ['apple', pd.Timedelta('1day')]
pd.to_timedelta(m, errors='coerce')
TimedeltaIndex([NaT, '1 days'], dtype='timedelta64[ns]', freq=None)
除了object转换,to_numeric()
还提供了一个downcast
参数,它可以将新的数字数据向下转换为更小的dtype,这样可以节省内存:
m = ['1', 2, 3]
pd.to_numeric(m, downcast='integer')
array([1, 2, 3], dtype=int8)
pd.to_numeric(m, downcast='signed')
array([1, 2, 3], dtype=int8)
pd.to_numeric(m, downcast='unsigned')
array([1, 2, 3], dtype=uint8)
pd.to_numeric(m, downcast='float')
array([1., 2., 3.], dtype=float32)
由于这些方法只适用于一维数组、列表或标量;它们不能直接用于多维对象(如DataFrames)。但是,使用apply(),我们可以在每一列上有效地“应用”这个函数:
import datetime
df = pd.DataFrame([
['2016-07-09', datetime.datetime(2016, 3, 2)]] * 2, dtype='O')
df
0 | 1 | |
---|---|---|
0 | 2016-07-09 | 2016-03-02 00:00:00 |
1 | 2016-07-09 | 2016-03-02 00:00:00 |
df.apply(pd.to_datetime)
0 | 1 | |
---|---|---|
0 | 2016-07-09 | 2016-03-02 |
1 | 2016-07-09 | 2016-03-02 |
df = pd.DataFrame([['1.1', 2, 3]] * 2, dtype='O')
df
0 | 1 | 2 | |
---|---|---|---|
0 | 1.1 | 2 | 3 |
1 | 1.1 | 2 | 3 |
df.apply(pd.to_numeric)
0 | 1 | 2 | |
---|---|---|---|
0 | 1.1 | 2 | 3 |
1 | 1.1 | 2 | 3 |
df = pd.DataFrame([['5us', pd.Timedelta('1day')]] * 2, dtype='O')
df
0 | 1 | |
---|---|---|
0 | 5us | 1 days 00:00:00 |
1 | 5us | 1 days 00:00:00 |
df.apply(pd.to_timedelta)
0 | 1 | |
---|---|---|
0 | 00:00:00.000005 | 1 days |
1 | 00:00:00.000005 | 1 days |
陷阱
对integer
类型执行操作可以很容易的向上转型为float
类型。
在不引入nans的情况下,将保留输入数据的dtype。
dfi = df3.astype('int32')
dfi['E'] =1
dfi
A | B | C | E | |
---|---|---|---|---|
0 | 0 | 0 | 0 | 1 |
1 | 0 | 0 | 0 | 1 |
2 | 0 | 0 | 255 | 1 |
3 | -1 | 0 | 1 | 1 |
4 | 0 | 0 | 0 | 1 |
5 | -1 | 0 | 0 | 1 |
6 | -1 | 0 | 255 | 1 |
7 | 0 | 0 | 0 | 1 |
dfi.dtypes
A int32
B int32
C int32
E int64
dtype: object
casted = dfi[dfi > 0]
casted.dtypes
A float64
B float64
C float64
E int64
dtype: object
可以看到,经过操作后,int32变成了float64, 但是如果数据原本是float32,操作后不会被转换成float64