处理缺失数据#
在本节中,我们将讨论 cudf 中的缺失值(也称为 NA
)。cudf 支持所有数据类型中的缺失值。这些缺失值由 <NA>
表示。这些值也被称为“null 值”。
如何检测缺失值#
要检测缺失值,可以使用 isna()
和 notna()
函数。
import cudf
import numpy as np
rng = np.random.default_rng()
df = cudf.DataFrame({"a": [1, 2, None, 4], "b": [0.1, None, 2.3, 17.17]})
df
a | b | |
---|---|---|
0 | 1 | 0.1 |
1 | 2 | <NA> |
2 | <NA> | 2.3 |
3 | 4 | 17.17 |
df.isna()
a | b | |
---|---|---|
0 | False | False |
1 | False | True |
2 | True | False |
3 | False | False |
df["a"].notna()
0 True
1 True
2 False
3 True
Name: a, dtype: bool
需要注意的是,在 Python(和 NumPy)中,nan 不相等,但 None 相等。请注意,cudf/NumPy 利用了 np.nan != np.nan
的事实,并将 None
视为 np.nan
。
None == None
True
np.nan == np.nan
False
因此,与上述情况相比,标量与 None/np.nan 的相等性比较不提供有用的信息。
df["b"] == np.nan
0 False
1 <NA>
2 False
3 False
Name: b, dtype: bool
s = cudf.Series([None, 1, 2])
s
0 <NA>
1 1
2 2
dtype: int64
s == None
0 <NA>
1 <NA>
2 <NA>
dtype: bool
s = cudf.Series([1, 2, np.nan], nan_as_null=False)
s
0 1.0
1 2.0
2 NaN
dtype: float64
s == np.nan
0 False
1 False
2 False
dtype: bool
浮点数据类型和缺失数据#
因为 NaN
是浮点数,即使只有单个缺失值的整型列也会被转换为浮点数据类型。然而,这默认不会发生。
默认情况下,如果将 NaN
值传递给 Series
构造函数,它将被视为 <NA>
值。
cudf.Series([1, 2, np.nan])
0 1
1 2
2 <NA>
dtype: int64
因此,要将 NaN
视为 NaN
,必须将 nan_as_null=False
参数传递给 Series
构造函数。
cudf.Series([1, 2, np.nan], nan_as_null=False)
0 1.0
1 2.0
2 NaN
dtype: float64
日期时间#
对于 datetime64
类型,cudf 不支持 NaT
值。相反,这些 numpy 和 pandas 特有的值在 cudf 中被视为 null 值 (<NA>
)。NaT
的实际底层值是 min(int64)
,并且 cudf 在将 cudf 对象转换为 pandas 对象时会保留此底层值。
import pandas as pd
datetime_series = cudf.Series(
[pd.Timestamp("20120101"), pd.NaT, pd.Timestamp("20120101")]
)
datetime_series
0 2012-01-01 00:00:00.000000000
1 NaT
2 2012-01-01 00:00:00.000000000
dtype: datetime64[ns]
datetime_series.to_pandas()
0 2012-01-01
1 NaT
2 2012-01-01
dtype: datetime64[ns]
对 datetime
列中包含 <NA>
值的行执行任何操作,将在结果列的相同位置产生 <NA>
值
datetime_series - datetime_series
0 0 days 00:00:00
1 NaT
2 0 days 00:00:00
dtype: timedelta64[ns]
包含缺失数据的计算#
Null 值通过 pandas 对象之间的算术运算自然传播。
df1 = cudf.DataFrame(
{
"a": [1, None, 2, 3, None],
"b": cudf.Series([np.nan, 2, 3.2, 0.1, 1], nan_as_null=False),
}
)
df2 = cudf.DataFrame(
{"a": [1, 11, 2, 34, 10], "b": cudf.Series([0.23, 22, 3.2, None, 1])}
)
df1
a | b | |
---|---|---|
0 | 1 | NaN |
1 | <NA> | 2.0 |
2 | 2 | 3.2 |
3 | 3 | 0.1 |
4 | <NA> | 1.0 |
df2
a | b | |
---|---|---|
0 | 1 | 0.23 |
1 | 11 | 22.0 |
2 | 2 | 3.2 |
3 | 34 | <NA> |
4 | 10 | 1.0 |
df1 + df2
a | b | |
---|---|---|
0 | 2 | NaN |
1 | <NA> | 24.0 |
2 | 4 | 6.4 |
3 | 37 | <NA> |
4 | <NA> | 2.0 |
在对 Series 中的数据求和时,NA
值将被视为 0
。
df1["a"]
0 1
1 <NA>
2 2
3 3
4 <NA>
Name: a, dtype: int64
df1["a"].sum()
np.int64(6)
由于 NA
值被视为 0
,在这种情况下,均值将为 2 (1 + 0 + 2 + 3 + 0)/5 = 2
df1["a"].mean()
np.float64(2.0)
为了在上述计算中保留 NA
值,sum
和 mean
支持 skipna
参数。默认情况下其值设置为 True
,我们可以将其更改为 False
以保留 NA
值。
df1["a"].sum(skipna=False)
np.float64(nan)
df1["a"].mean(skipna=False)
np.float64(nan)
默认情况下,累积方法(如 cumsum
和 cumprod
)会忽略 NA
值。
df1["a"].cumsum()
0 1
1 <NA>
2 3
3 6
4 <NA>
Name: a, dtype: int64
要在累积方法中保留 NA
值,请提供 skipna=False
。
df1["a"].cumsum(skipna=False)
0 1
1 <NA>
2 <NA>
3 <NA>
4 <NA>
Name: a, dtype: int64
Null/nan 的求和/求积#
空的或全部为 NA 的 DataFrame Series 的和为 0。
cudf.Series([np.nan], nan_as_null=False).sum()
np.float64(0.0)
cudf.Series([np.nan], nan_as_null=False).sum(skipna=False)
np.float64(nan)
cudf.Series([], dtype="float64").sum()
np.float64(0.0)
空的或全部为 NA 的 DataFrame Series 的积为 1。
cudf.Series([np.nan], nan_as_null=False).prod()
np.float64(1.0)
cudf.Series([np.nan], nan_as_null=False).prod(skipna=False)
np.float64(nan)
cudf.Series([], dtype="float64").prod()
np.float64(1.0)
GroupBy 中的 NA 值#
默认情况下,GroupBy 中的 NA
组会被自动排除。例如
df1
a | b | |
---|---|---|
0 | 1 | NaN |
1 | <NA> | 2.0 |
2 | 2 | 3.2 |
3 | 3 | 0.1 |
4 | <NA> | 1.0 |
df1.groupby("a").mean()
b | |
---|---|
a | |
3 | 0.1 |
1 | NaN |
2 | 3.2 |
也可以通过传递 dropna=False
将 NA
包含在组中
df1.groupby("a", dropna=False).mean()
b | |
---|---|
a | |
3 | 0.1 |
1 | NaN |
2 | 3.2 |
<NA> | 1.5 |
插入缺失数据#
所有数据类型都支持通过赋值插入缺失值。Series 中的任何特定位置都可以通过将其赋值为 None
来设为 null。
series = cudf.Series([1, 2, 3, 4])
series
0 1
1 2
2 3
3 4
dtype: int64
series[2] = None
series
0 1
1 2
2 <NA>
3 4
dtype: int64
填充缺失值: fillna#
fillna()
可以用非 NA 数据填充 NA
和 NaN
值。
df1
a | b | |
---|---|---|
0 | 1 | NaN |
1 | <NA> | 2.0 |
2 | 2 | 3.2 |
3 | 3 | 0.1 |
4 | <NA> | 1.0 |
df1["b"].fillna(10)
0 10.0
1 2.0
2 3.2
3 0.1
4 1.0
Name: b, dtype: float64
用 cudf 对象填充#
您还可以使用可对齐的 dict 或 Series 来 fillna。dict 的标签或 Series 的索引必须与您希望填充的 frame 的列匹配。这种用法的场景是使用该列的平均值填充 DataFrame。
import cupy as cp
cp_rng = cp.random.default_rng()
dff = cudf.DataFrame(cp_rng.standard_normal((10, 3)), columns=list("ABC"))
dff.iloc[3:5, 0] = np.nan
dff.iloc[4:6, 1] = np.nan
dff.iloc[5:8, 2] = np.nan
dff
A | B | C | |
---|---|---|---|
0 | 0.974978 | 0.282769 | 2.254196 |
1 | 1.863146 | -0.514037 | -0.047407 |
2 | -0.979084 | 0.127691 | 0.713527 |
3 | NaN | -0.245523 | -0.000601 |
4 | NaN | NaN | 0.198238 |
5 | 0.006034 | NaN | NaN |
6 | 1.139740 | -1.005057 | NaN |
7 | 0.542860 | -0.802587 | NaN |
8 | 0.669212 | -1.225563 | 0.831125 |
9 | 0.407667 | -0.851054 | -0.334266 |
dff.fillna(dff.mean())
A | B | C | |
---|---|---|---|
0 | 0.974978 | 0.282769 | 2.254196 |
1 | 1.863146 | -0.514037 | -0.047407 |
2 | -0.979084 | 0.127691 | 0.713527 |
3 | 0.578069 | -0.245523 | -0.000601 |
4 | 0.578069 | -0.529170 | 0.198238 |
5 | 0.006034 | -0.529170 | 0.516402 |
6 | 1.139740 | -1.005057 | 0.516402 |
7 | 0.542860 | -0.802587 | 0.516402 |
8 | 0.669212 | -1.225563 | 0.831125 |
9 | 0.407667 | -0.851054 | -0.334266 |
dff.fillna(dff.mean()[1:3])
A | B | C | |
---|---|---|---|
0 | 0.974978 | 0.282769 | 2.254196 |
1 | 1.863146 | -0.514037 | -0.047407 |
2 | -0.979084 | 0.127691 | 0.713527 |
3 | NaN | -0.245523 | -0.000601 |
4 | NaN | -0.529170 | 0.198238 |
5 | 0.006034 | -0.529170 | 0.516402 |
6 | 1.139740 | -1.005057 | 0.516402 |
7 | 0.542860 | -0.802587 | 0.516402 |
8 | 0.669212 | -1.225563 | 0.831125 |
9 | 0.407667 | -0.851054 | -0.334266 |
删除包含缺失数据的轴标签: dropna#
可以使用 dropna()
排除缺失数据
df1
a | b | |
---|---|---|
0 | 1 | NaN |
1 | <NA> | 2.0 |
2 | 2 | 3.2 |
3 | 3 | 0.1 |
4 | <NA> | 1.0 |
df1.dropna(axis=0)
a | b | |
---|---|---|
2 | 2 | 3.2 |
3 | 3 | 0.1 |
df1.dropna(axis=1)
0 |
---|
1 |
2 |
3 |
4 |
对于 Series,也有等效的 dropna()
可用。
df1["a"].dropna()
0 1
2 2
3 3
Name: a, dtype: int64
替换通用值#
通常我们希望用其他值替换任意值。
Series 中的 replace()
和 DataFrame 中的 replace()
提供了一种高效且灵活的方式来执行此类替换。
series = cudf.Series([0.0, 1.0, 2.0, 3.0, 4.0])
series
0 0.0
1 1.0
2 2.0
3 3.0
4 4.0
dtype: float64
series.replace(0, 5)
0 5.0
1 1.0
2 2.0
3 3.0
4 4.0
dtype: float64
我们还可以将任何值替换为 <NA>
值。
series.replace(0, None)
0 <NA>
1 1.0
2 2.0
3 3.0
4 4.0
dtype: float64
您可以使用一个值列表替换另一个值列表
series.replace([0, 1, 2, 3, 4], [4, 3, 2, 1, 0])
0 4.0
1 3.0
2 2.0
3 1.0
4 0.0
dtype: float64
您也可以指定一个映射字典
series.replace({0: 10, 1: 100})
0 10.0
1 100.0
2 2.0
3 3.0
4 4.0
dtype: float64
对于 DataFrame,您可以按列指定单个值
df = cudf.DataFrame({"a": [0, 1, 2, 3, 4], "b": [5, 6, 7, 8, 9]})
df
a | b | |
---|---|---|
0 | 0 | 5 |
1 | 1 | 6 |
2 | 2 | 7 |
3 | 3 | 8 |
4 | 4 | 9 |
df.replace({"a": 0, "b": 5}, 100)
a | b | |
---|---|---|
0 | 100 | 100 |
1 | 1 | 6 |
2 | 2 | 7 |
3 | 3 | 8 |
4 | 4 | 9 |
字符串/正则表达式替换#
cudf 支持使用 replace
API 替换字符串值
d = {"a": list(range(4)), "b": list("ab.."), "c": ["a", "b", None, "d"]}
df = cudf.DataFrame(d)
df
a | b | c | |
---|---|---|---|
0 | 0 | a | a |
1 | 1 | b | b |
2 | 2 | . | <NA> |
3 | 3 | . | d |
df.replace(".", "A Dot")
a | b | c | |
---|---|---|---|
0 | 0 | a | a |
1 | 1 | b | b |
2 | 2 | A Dot | <NA> |
3 | 3 | A Dot | d |
df.replace([".", "b"], ["A Dot", None])
a | b | c | |
---|---|---|---|
0 | 0 | a | a |
1 | 1 | <NA> | <NA> |
2 | 2 | A Dot | <NA> |
3 | 3 | A Dot | d |
替换几个不同的值 (列表 -> 列表)
df.replace(["a", "."], ["b", "--"])
a | b | c | |
---|---|---|---|
0 | 0 | b | b |
1 | 1 | b | b |
2 | 2 | -- | <NA> |
3 | 3 | -- | d |
仅在列 ‘b’ 中搜索 (字典 -> 字典)
df.replace({"b": "."}, {"b": "replacement value"})
a | b | c | |
---|---|---|---|
0 | 0 | a | a |
1 | 1 | b | b |
2 | 2 | 替换值 | <NA> |
3 | 3 | 替换值 | d |
数值替换#
replace()
也可以像 fillna()
一样使用。
df = cudf.DataFrame(cp_rng.standard_normal((10, 2)))
df[rng.random(df.shape[0]) > 0.5] = 1.5
df.replace(1.5, None)
0 | 1 | |
---|---|---|
0 | -0.272310839 | -0.753041385 |
1 | 0.450345774 | -0.010457805 |
2 | -0.423440013 | -0.503168227 |
3 | 0.921114386 | -1.263712911 |
4 | <NA> | <NA> |
5 | -0.209139866 | -0.936384873 |
6 | 0.548776457 | -0.488282384 |
7 | <NA> | <NA> |
8 | <NA> | <NA> |
9 | <NA> | <NA> |
通过传递列表可以替换多个值。
df00 = df.iloc[0, 0]
df.replace([1.5, df00], [5, 10])
0 | 1 | |
---|---|---|
0 | 10.000000 | -0.753041 |
1 | 0.450346 | -0.010458 |
2 | -0.423440 | -0.503168 |
3 | 0.921114 | -1.263713 |
4 | 5.000000 | 5.000000 |
5 | -0.209140 | -0.936385 |
6 | 0.548776 | -0.488282 |
7 | 5.000000 | 5.000000 |
8 | 5.000000 | 5.000000 |
9 | 5.000000 | 5.000000 |
您还可以在 DataFrame 原位操作
df.replace(1.5, None, inplace=True)
df
0 | 1 | |
---|---|---|
0 | -0.272310839 | -0.753041385 |
1 | 0.450345774 | -0.010457805 |
2 | -0.423440013 | -0.503168227 |
3 | 0.921114386 | -1.263712911 |
4 | <NA> | <NA> |
5 | -0.209139866 | -0.936384873 |
6 | 0.548776457 | -0.488282384 |
7 | <NA> | <NA> |
8 | <NA> | <NA> |
9 | <NA> | <NA> |