常见问题(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 -语句或在使用布尔运算时: andor ,以及 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引入现有的 SeriesDataFrame 通过 reindex() 或者一些其他手段,为了存储NAS,布尔和整数类型将被提升为不同的数据类型。此表汇总了促销活动:

类型类

用于存储NAS的升级数据类型

floating

没有变化

object

没有变化

integer

投给 float64

boolean

投给 object

虽然这看起来可能是一种很大的权衡,但我发现在实践中这是一个问题的情况很少,即存储大于2**53的值。关于动机的一些解释将在下一节中介绍。

为什么不让NumPy像R一样呢?#

许多人建议NumPy应该简单地效仿 NA support present in the more domain-specific statistical programming language R 。部分原因是NumPy类型层次结构:

类型类

数据类型

numpy.floating

float16, float32, float64, float128

numpy.integer

int8, int16, int32, int64

numpy.unsignedinteger

uint8, uint16, uint32, uint64

numpy.object_

object_

numpy.bool_

bool_

numpy.character

string_, unicode_

相比之下,R语言只有几种内置数据类型: integernumeric (浮点)、 character ,以及 booleanNA 类型是通过为用作缺失值的每个类型保留特殊位模式来实现的。虽然使用完整的NumPy类型层次结构可以做到这一点,但这将是一个更重要的权衡(特别是对于8位和16位数据类型)和实现。

另一种方法是使用掩码阵列。掩码数组是具有关联布尔值的数据数组 mask 指示是否应考虑每个值 NA 或者不去。我个人并不喜欢这种方法,因为我觉得总的来说,它给用户和库实现者带来了相当沉重的负担。此外,与使用简单的方法相比,在处理数值数据时,它需要相当高的性能成本 NaN 。因此,我选择了毕德学派的“实用胜于纯洁”的方法,并交换了整数 NA 能够使用浮点数组和对象数组中的特殊值来表示更简单的方法 NA ,并在必须引入NAS时将整数数组提升为浮点数组。

与NumPy的区别#

SeriesDataFrame 对象, 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数组转换为本机系统字节顺序 在此之前 把它传给 SeriesDataFrame 构造函数使用与以下内容类似的内容:

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 了解更多详细信息。