常见问题(FAQ)#
DataFrame内存使用情况#
的内存使用情况 DataFrame
(包括索引)在调用 info()
。配置选项, display.memory_usage
(请参阅 the list of options ),指定是否 DataFrame
调用时将显示内存使用情况 df.info()
方法。
例如, DataFrame
调用时显示如下 info()
:
In [1]: dtypes = [
...: "int64",
...: "float64",
...: "datetime64[ns]",
...: "timedelta64[ns]",
...: "complex128",
...: "object",
...: "bool",
...: ]
...:
In [2]: n = 5000
In [3]: data = {t: np.random.randint(100, size=n).astype(t) for t in dtypes}
In [4]: df = pd.DataFrame(data)
In [5]: df["categorical"] = df["object"].astype("category")
In [6]: df.info()
<class 'pandas.core.frame.DataFrame'>
RangeIndex: 5000 entries, 0 to 4999
Data columns (total 8 columns):
# Column Non-Null Count Dtype
--- ------ -------------- -----
0 int64 5000 non-null int64
1 float64 5000 non-null float64
2 datetime64[ns] 5000 non-null datetime64[ns]
3 timedelta64[ns] 5000 non-null timedelta64[ns]
4 complex128 5000 non-null complex128
5 object 5000 non-null object
6 bool 5000 non-null bool
7 categorical 5000 non-null category
dtypes: bool(1), category(1), complex128(1), datetime64[ns](1), float64(1), int64(1), object(1), timedelta64[ns](1)
memory usage: 288.2+ KB
这个 +
符号指示实际的内存使用率可能更高,因为Pandas不计算 dtype=object
。
通过 memory_usage='deep'
将启用更准确的内存使用报告,说明所包含对象的全部使用情况。这是可选的,因为进行这种更深层次的自省可能代价高昂。
In [7]: df.info(memory_usage="deep")
<class 'pandas.core.frame.DataFrame'>
RangeIndex: 5000 entries, 0 to 4999
Data columns (total 8 columns):
# Column Non-Null Count Dtype
--- ------ -------------- -----
0 int64 5000 non-null int64
1 float64 5000 non-null float64
2 datetime64[ns] 5000 non-null datetime64[ns]
3 timedelta64[ns] 5000 non-null timedelta64[ns]
4 complex128 5000 non-null complex128
5 object 5000 non-null object
6 bool 5000 non-null bool
7 categorical 5000 non-null category
dtypes: bool(1), category(1), complex128(1), datetime64[ns](1), float64(1), int64(1), object(1), timedelta64[ns](1)
memory usage: 424.7 KB
默认情况下,显示选项设置为 True
但可以通过将 memory_usage
调用时的参数 df.info()
。
每列的内存使用情况可以通过调用 memory_usage()
方法。这将返回一个 Series
其中索引由列名和以字节表示的每列的内存使用来表示。对于 DataFrame
上面,每列的内存使用情况和总内存使用情况可以通过 memory_usage
方法:
In [8]: df.memory_usage()
Out[8]:
Index 128
int64 40000
float64 40000
datetime64[ns] 40000
timedelta64[ns] 40000
complex128 80000
object 40000
bool 5000
categorical 9968
dtype: int64
# total memory usage of dataframe
In [9]: df.memory_usage().sum()
Out[9]: 295096
默认情况下, DataFrame
索引显示在返回的 Series
,则可以通过传递 index=False
论点:
In [10]: df.memory_usage(index=False)
Out[10]:
int64 40000
float64 40000
datetime64[ns] 40000
timedelta64[ns] 40000
complex128 80000
object 40000
bool 5000
categorical 9968
dtype: int64
对象显示的内存使用情况 info()
方法利用 memory_usage()
方法来确定 DataFrame
同时还以人类可读的单位格式化输出(基数2表示;即1KB=1024字节)。
另请参阅 Categorical Memory Usage 。
对Pandas使用IF/TRUE语句#
Pandas遵循NumPy约定,即在尝试将某些内容转换为 bool
。这发生在 if
-语句或在使用布尔运算时: and
, or
,以及 not
。目前尚不清楚以下代码的结果应该是什么:
>>> if pd.Series([False, True, False]):
... pass
应该是吗? True
因为它不是零长度,或者 False
因为有很多 False
价值观?目前还不清楚,所以Pandas提出了一个 ValueError
:
In [11]: if pd.Series([False, True, False]):
....: print("I was true")
....:
---------------------------------------------------------------------------
ValueError Traceback (most recent call last)
Input In [11], in <cell line: 1>()
----> 1 if pd.Series([False, True, False]):
2 print("I was true")
File /usr/local/lib/python3.10/dist-packages/pandas-1.5.0.dev0+697.gf9762d8f52-py3.10-linux-x86_64.egg/pandas/core/generic.py:1417, in NDFrame.__nonzero__(self)
1415 @final
1416 def __nonzero__(self):
-> 1417 raise ValueError(
1418 f"The truth value of a {type(self).__name__} is ambiguous. "
1419 "Use a.empty, a.bool(), a.item(), a.any() or a.all()."
1420 )
ValueError: The truth value of a Series is ambiguous. Use a.empty, a.bool(), a.item(), a.any() or a.all().
您需要显式选择要如何处理 DataFrame
,例如,使用 any()
, all()
或 empty()
。或者,您可能希望比较Pandas对象是否为 None
:
In [12]: if pd.Series([False, True, False]) is not None:
....: print("I was not None")
....:
I was not None
下面是如何检查是否有任何值是 True
:
In [13]: if pd.Series([False, True, False]).any():
....: print("I am any")
....:
I am any
要在布尔上下文中计算单个元素的Pandas对象,请使用方法 bool()
:
In [14]: pd.Series([True]).bool()
Out[14]: True
In [15]: pd.Series([False]).bool()
Out[15]: False
In [16]: pd.DataFrame([[True]]).bool()
Out[16]: True
In [17]: pd.DataFrame([[False]]).bool()
Out[17]: False
按位布尔值#
按位布尔运算符,如 ==
和 !=
返回布尔值 Series
它在与标量进行比较时执行逐个元素的比较。
In [18]: s = pd.Series(range(5))
In [19]: s == 4
Out[19]:
0 False
1 False
2 False
3 False
4 True
dtype: bool
看见 boolean comparisons 查看更多示例。
使用 in
操作员#
使用Python. in
运算符 Series
的会员资格测试 索引 ,而不是价值观中的成员资格。
In [20]: s = pd.Series(range(5), index=list("abcde"))
In [21]: 2 in s
Out[21]: False
In [22]: 'b' in s
Out[22]: True
如果这种行为令人惊讶,请记住使用 in
测试键,而不是值,以及 Series
就像字典一样。若要测试值中的成员身份,请使用 isin()
:
In [23]: s.isin([2])
Out[23]:
a False
b False
c True
d False
e False
dtype: bool
In [24]: s.isin([2]).any()
Out[24]: True
为 DataFrame
,同样, in
应用于列轴,测试列名列表中的成员身份。
使用用户定义函数(UDF)方法进行变异#
本节适用于采用UDF的Pandas方法。特别是,这些方法 .apply
, .aggregate
, .transform
,以及 .filter
。
在编程中的一般规则是,当容器被迭代时,不应该改变它。突变将使迭代器失效,导致意外行为。请考虑下面的示例:
In [25]: values = [0, 1, 2, 3, 4, 5]
In [26]: n_removed = 0
In [27]: for k, value in enumerate(values):
....: idx = k - n_removed
....: if value % 2 == 1:
....: del values[idx]
....: n_removed += 1
....: else:
....: values[idx] = value + 1
....:
In [28]: values
Out[28]: [1, 4, 5]
人们可能会预料到结果会是 [1, 3, 5]
。在使用接受UDF的Pandas方法时,Pandas通常在内部迭代 DataFrame
或其他大Pandas反对。因此,如果UDF发生变化(更改), DataFrame
,则可能会出现意外行为。
下面是一个类似的示例 DataFrame.apply()
:
In [29]: def f(s):
....: s.pop("a")
....: return s
....:
In [30]: df = pd.DataFrame({"a": [1, 2, 3], "b": [4, 5, 6]})
In [31]: try:
....: df.apply(f, axis="columns")
....: except Exception as err:
....: print(repr(err))
....:
KeyError('a')
要解决这个问题,可以复制一份副本,这样突变就不会应用于正在迭代的容器。
In [32]: values = [0, 1, 2, 3, 4, 5]
In [33]: n_removed = 0
In [34]: for k, value in enumerate(values.copy()):
....: idx = k - n_removed
....: if value % 2 == 1:
....: del values[idx]
....: n_removed += 1
....: else:
....: values[idx] = value + 1
....:
In [35]: values
Out[35]: [1, 3, 5]
In [36]: def f(s):
....: s = s.copy()
....: s.pop("a")
....: return s
....:
In [37]: df = pd.DataFrame({"a": [1, 2, 3], 'b': [4, 5, 6]})
In [38]: df.apply(f, axis="columns")
Out[38]:
b
0 4
1 5
2 6
NaN
,Integer NA
值和 NA
类型促销#
选择 NA
表示法#
因为缺少 NA
(缺少)NumPy和Python中从头开始的支持总的来说,我们面临着在以下两者之间做出艰难的选择:
A 掩蔽阵列 解决方案:一个数据数组和一个布尔值数组,用于指示值是否存在。
使用特殊的标记值、位模式或一组标记值来表示
NA
跨数据类型。
出于许多原因,我们选择了后者。经过多年的生产使用,至少在我看来,考虑到NumPy和Python的总体情况,它被证明是最好的决定。特殊的价值 NaN
(非A数字)在任何地方都用作 NA
值,并且有API函数 DataFrame.isna()
和 DataFrame.notna()
其可以跨数据类型使用以检测NA值。
然而,随之而来的是几个我肯定没有忽视的权衡。
支持整型 NA
#
在没有高性能的情况下 NA
对NumPy从头开始的支持,主要的损失是以整数数组表示Nas的能力。例如:
In [39]: s = pd.Series([1, 2, 3, 4, 5], index=list("abcde"))
In [40]: s
Out[40]:
a 1
b 2
c 3
d 4
e 5
dtype: int64
In [41]: s.dtype
Out[41]: dtype('int64')
In [42]: s2 = s.reindex(["a", "b", "c", "f", "u"])
In [43]: s2
Out[43]:
a 1.0
b 2.0
c 3.0
f NaN
u NaN
dtype: float64
In [44]: s2.dtype
Out[44]: dtype('float64')
这种权衡很大程度上是出于内存和性能的原因,也是为了 Series
仍然是“数字的”。
如果需要表示可能缺少值的整数,请使用Pandas提供的可为空的整型扩展数据类型之一
In [45]: s_int = pd.Series([1, 2, 3, 4, 5], index=list("abcde"), dtype=pd.Int64Dtype())
In [46]: s_int
Out[46]:
a 1
b 2
c 3
d 4
e 5
dtype: Int64
In [47]: s_int.dtype
Out[47]: Int64Dtype()
In [48]: s2_int = s_int.reindex(["a", "b", "c", "f", "u"])
In [49]: s2_int
Out[49]:
a 1
b 2
c 3
f <NA>
u <NA>
dtype: Int64
In [50]: s2_int.dtype
Out[50]: Int64Dtype()
看见 可为空的整型数据类型 想要更多。
NA
类型促销#
将NAS引入现有的 Series
或 DataFrame
通过 reindex()
或者一些其他手段,为了存储NAS,布尔和整数类型将被提升为不同的数据类型。此表汇总了促销活动:
类型类 |
用于存储NAS的升级数据类型 |
---|---|
|
没有变化 |
|
没有变化 |
|
投给 |
|
投给 |
虽然这看起来可能是一种很大的权衡,但我发现在实践中这是一个问题的情况很少,即存储大于2**53的值。关于动机的一些解释将在下一节中介绍。
为什么不让NumPy像R一样呢?#
许多人建议NumPy应该简单地效仿 NA
support present in the more domain-specific statistical programming language R 。部分原因是NumPy类型层次结构:
类型类 |
数据类型 |
---|---|
|
|
|
|
|
|
|
|
|
|
|
|
相比之下,R语言只有几种内置数据类型: integer
, numeric
(浮点)、 character
,以及 boolean
。 NA
类型是通过为用作缺失值的每个类型保留特殊位模式来实现的。虽然使用完整的NumPy类型层次结构可以做到这一点,但这将是一个更重要的权衡(特别是对于8位和16位数据类型)和实现。
另一种方法是使用掩码阵列。掩码数组是具有关联布尔值的数据数组 mask 指示是否应考虑每个值 NA
或者不去。我个人并不喜欢这种方法,因为我觉得总的来说,它给用户和库实现者带来了相当沉重的负担。此外,与使用简单的方法相比,在处理数值数据时,它需要相当高的性能成本 NaN
。因此,我选择了毕德学派的“实用胜于纯洁”的方法,并交换了整数 NA
能够使用浮点数组和对象数组中的特殊值来表示更简单的方法 NA
,并在必须引入NAS时将整数数组提升为浮点数组。
与NumPy的区别#
为 Series
和 DataFrame
对象, var()
规格化方式 N-1
以产生样本方差的无偏估计,而NumPy的 numpy.var()
按N进行规格化,N用于测量样本的方差。请注意 cov()
规格化方式 N-1
在Pandas和NumPy中都有。
线程安全#
Pandas并不是100%的线程安全的。已知的问题与 copy()
方法。如果您要大量复制 DataFrame
对象时,我们建议在发生数据复制的线程内持有锁。
看见 this link 了解更多信息。
字节排序问题#
有时,您可能不得不处理在一台机器上创建的数据,该机器的字节顺序与您正在运行的那台机器的字节顺序不同。此问题的常见症状是错误,如:
Traceback
...
ValueError: Big-endian buffer not supported on little-endian compiler
要处理此问题,应将基础NumPy数组转换为本机系统字节顺序 在此之前 把它传给 Series
或 DataFrame
构造函数使用与以下内容类似的内容:
In [51]: x = np.array(list(range(10)), ">i4") # big endian
In [52]: newx = x.byteswap().newbyteorder() # force native byteorder
In [53]: s = pd.Series(newx)
看见 the NumPy documentation on byte order 了解更多详细信息。