常见问题 (FAQ) # 数据帧内存使用情况# 调用 .a 时会显示a 的内存使用情况DataFrame(包括索引)info()。配置选项display.memory_usage (请参阅选项列表)指定 DataFrame调用该方法时是否显示内存使用情况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在调用时传递参数来显式覆盖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 字节)格式化输出。 另请参阅分类内存使用情况。 在 pandas 中使用 if/truth 语句# 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) <ipython-input-11-5c782b38cd2f> in ?() ----> 1 if pd.Series([False, True, False]): 2 print("I was true") ~/work/pandas/pandas/pandas/core/generic.py in ?(self) 1575 @final 1576 def __nonzero__(self) -> NoReturn: -> 1577 raise ValueError( 1578 f"The truth value of a {type(self).__name__} is ambiguous. " 1579 "Use a.empty, a.bool(), a.item(), a.any() or a.all()." 1580 ) 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 按位布尔值# 按位布尔运算符类似于==和!=返回一个布尔值Series ,该布尔值在与标量比较时执行按元素比较。 In [14]: s = pd.Series(range(5)) In [15]: s == 4 Out[15]: 0 False 1 False 2 False 3 False 4 True dtype: bool 有关更多示例,请参阅布尔比较。 使用in运算符# 使用 Pythonin运算符测试索引Series中的成员资格 ,而不是值之间的成员资格。 In [16]: s = pd.Series(range(5), index=list("abcde")) In [17]: 2 in s Out[17]: False In [18]: 'b' in s Out[18]: True 如果这种行为令人惊讶,请记住,in在 Python 字典上使用测试键,而不是值,并且Series类似于字典。要测试值的成员资格,请使用以下方法isin(): In [19]: s.isin([2]) Out[19]: a False b False c True d False e False dtype: bool In [20]: s.isin([2]).any() Out[20]: True 同样DataFrame,in适用于列轴,测试列名称列表中的成员资格。 使用用户定义函数 (UDF) 方法进行变异# 本节适用于采用 UDF 的 pandas 方法。特别是方法 DataFrame.apply()、DataFrame.aggregate()、DataFrame.transform()和 DataFrame.filter()。 编程中的一条一般规则是,在迭代容器时不应改变容器。突变会使迭代器失效,从而导致意外的行为。考虑这个例子: In [21]: values = [0, 1, 2, 3, 4, 5] In [22]: n_removed = 0 In [23]: 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 [24]: values Out[24]: [1, 4, 5] 也许有人会预料到结果会是这样。当使用采用 UDF 的 pandas 方法时,pandas 内部通常会迭代该 或其他 pandas 对象。因此,如果 UDF 变异(更改),可能会出现意外行为。[1, 3, 5]DataFrameDataFrame 这是一个类似的例子DataFrame.apply(): In [25]: def f(s): ....: s.pop("a") ....: return s ....: In [26]: df = pd.DataFrame({"a": [1, 2, 3], "b": [4, 5, 6]}) In [27]: df.apply(f, axis="columns") --------------------------------------------------------------------------- KeyError Traceback (most recent call last) File ~/work/pandas/pandas/pandas/core/indexes/base.py:3805, in Index.get_loc(self, key) 3804 try: -> 3805 return self._engine.get_loc(casted_key) 3806 except KeyError as err: File index.pyx:167, in pandas._libs.index.IndexEngine.get_loc() File index.pyx:196, in pandas._libs.index.IndexEngine.get_loc() File pandas/_libs/hashtable_class_helper.pxi:7081, in pandas._libs.hashtable.PyObjectHashTable.get_item() File pandas/_libs/hashtable_class_helper.pxi:7089, in pandas._libs.hashtable.PyObjectHashTable.get_item() KeyError: 'a' The above exception was the direct cause of the following exception: KeyError Traceback (most recent call last) Cell In[27], line 1 ----> 1 df.apply(f, axis="columns") File ~/work/pandas/pandas/pandas/core/frame.py:10374, in DataFrame.apply(self, func, axis, raw, result_type, args, by_row, engine, engine_kwargs, **kwargs) 10360 from pandas.core.apply import frame_apply 10362 op = frame_apply( 10363 self, 10364 func=func, (...) 10372 kwargs=kwargs, 10373 ) > 10374 return op.apply().__finalize__(self, method="apply") File ~/work/pandas/pandas/pandas/core/apply.py:916, in FrameApply.apply(self) 913 elif self.raw: 914 return self.apply_raw(engine=self.engine, engine_kwargs=self.engine_kwargs) --> 916 return self.apply_standard() File ~/work/pandas/pandas/pandas/core/apply.py:1063, in FrameApply.apply_standard(self) 1061 def apply_standard(self): 1062 if self.engine == "python": -> 1063 results, res_index = self.apply_series_generator() 1064 else: 1065 results, res_index = self.apply_series_numba() File ~/work/pandas/pandas/pandas/core/apply.py:1081, in FrameApply.apply_series_generator(self) 1078 with option_context("mode.chained_assignment", None): 1079 for i, v in enumerate(series_gen): 1080 # ignore SettingWithCopy here in case the user mutates -> 1081 results[i] = self.func(v, *self.args, **self.kwargs) 1082 if isinstance(results[i], ABCSeries): 1083 # If we have a view on v, we need to make a copy because 1084 # series_generator will swap out the underlying data 1085 results[i] = results[i].copy(deep=False) Cell In[25], line 2, in f(s) 1 def f(s): ----> 2 s.pop("a") 3 return s File ~/work/pandas/pandas/pandas/core/series.py:5391, in Series.pop(self, item) 5366 def pop(self, item: Hashable) -> Any: 5367 """ 5368 Return item and drops from series. Raise KeyError if not found. 5369 (...) 5389 dtype: int64 5390 """ -> 5391 return super().pop(item=item) File ~/work/pandas/pandas/pandas/core/generic.py:947, in NDFrame.pop(self, item) 946 def pop(self, item: Hashable) -> Series | Any: --> 947 result = self[item] 948 del self[item] 950 return result File ~/work/pandas/pandas/pandas/core/series.py:1121, in Series.__getitem__(self, key) 1118 return self._values[key] 1120 elif key_is_scalar: -> 1121 return self._get_value(key) 1123 # Convert generator to list before going through hashable part 1124 # (We will iterate through the generator there to check for slices) 1125 if is_iterator(key): File ~/work/pandas/pandas/pandas/core/series.py:1237, in Series._get_value(self, label, takeable) 1234 return self._values[label] 1236 # Similar to Index.get_value, but we do not fall back to positional -> 1237 loc = self.index.get_loc(label) 1239 if is_integer(loc): 1240 return self._values[loc] File ~/work/pandas/pandas/pandas/core/indexes/base.py:3812, in Index.get_loc(self, key) 3807 if isinstance(casted_key, slice) or ( 3808 isinstance(casted_key, abc.Iterable) 3809 and any(isinstance(x, slice) for x in casted_key) 3810 ): 3811 raise InvalidIndexError(key) -> 3812 raise KeyError(key) from err 3813 except TypeError: 3814 # If we have a listlike key, _check_indexing_error will raise 3815 # InvalidIndexError. Otherwise we fall through and re-raise 3816 # the TypeError. 3817 self._check_indexing_error(key) KeyError: 'a' 要解决此问题,可以制作一个副本,以便突变不会应用于正在迭代的容器。 In [28]: values = [0, 1, 2, 3, 4, 5] In [29]: n_removed = 0 In [30]: 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 [31]: values Out[31]: [1, 3, 5] In [32]: def f(s): ....: s = s.copy() ....: s.pop("a") ....: return s ....: In [33]: df = pd.DataFrame({"a": [1, 2, 3], 'b': [4, 5, 6]}) In [34]: df.apply(f, axis="columns") Out[34]: b 0 4 1 5 2 6 NumPy 类型的缺失值表示# np.nan作为NANumPy 类型的表示# NA由于 NumPy 和 Python 一般缺乏(缺失)支持,NA可以用以下形式表示: 掩码数组解决方案:一个数据数组和一个布尔值数组,指示某个值是否存在或缺失。 使用特殊的哨兵值、位模式或哨兵值集来表示NA跨数据类型。 np.nan选择特殊值(Not-A-Number) 作为NANumPy 类型的值,并且有诸如DataFrame.isna()和 之类的 API 函数DataFrame.notna()可以在数据类型中使用来检测 NA 值。但是,此选择有一个缺点,即会将缺失的整数数据强制转换为浮点类型,如支持整数 NA中所示。 NANumPy 类型的类型提升# 当将 NA 引入现有Series或DataFrame通过 reindex()或其他方式时,布尔类型和整数类型将提升为不同的数据类型以存储 NA。促销活动总结如下表: 类型类 用于存储 NA 的促销 dtype floating 不用找了 object 不用找了 integer 投射到float64 boolean 投射到object 支持整数NA# 由于 NumPy 缺乏NA从头开始内置的高性能支持,主要的受害者是在整数数组中表示 NA 的能力。例如: In [35]: s = pd.Series([1, 2, 3, 4, 5], index=list("abcde")) In [36]: s Out[36]: a 1 b 2 c 3 d 4 e 5 dtype: int64 In [37]: s.dtype Out[37]: dtype('int64') In [38]: s2 = s.reindex(["a", "b", "c", "f", "u"]) In [39]: s2 Out[39]: a 1.0 b 2.0 c 3.0 f NaN u NaN dtype: float64 In [40]: s2.dtype Out[40]: dtype('float64') 这种权衡主要是出于内存和性能的原因,并且也是为了使结果Series仍然是“数字”。 如果您需要表示可能缺少值的整数,请使用 pandas 或 pyarrow 提供的可空整数扩展数据类型之一 Int8Dtype Int16Dtype Int32Dtype Int64Dtype ArrowDtype In [41]: s_int = pd.Series([1, 2, 3, 4, 5], index=list("abcde"), dtype=pd.Int64Dtype()) In [42]: s_int Out[42]: a 1 b 2 c 3 d 4 e 5 dtype: Int64 In [43]: s_int.dtype Out[43]: Int64Dtype() In [44]: s2_int = s_int.reindex(["a", "b", "c", "f", "u"]) In [45]: s2_int Out[45]: a 1 b 2 c 3 f <NA> u <NA> dtype: Int64 In [46]: s2_int.dtype Out[46]: Int64Dtype() In [47]: s_int_pa = pd.Series([1, 2, None], dtype="int64[pyarrow]") In [48]: s_int_pa Out[48]: 0 1 1 2 2 <NA> dtype: int64[pyarrow] 有关更多信息,请参阅可为空整数数据类型和PyArrow 功能。 为什么不让 NumPy 像 R 一样呢?# 许多人建议 NumPy 应该简单地模拟NA更特定领域的统计编程语言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 bytes_, str_ 相比之下,R 语言只有少数内置数据类型: integer、numeric(浮点)、character和 boolean。NA类型是通过为每种类型保留特殊的位模式来用作缺失值来实现的。虽然使用完整的 NumPy 类型层次结构来完成此操作是可能的,但这将是一个更重大的权衡(特别是对于 8 位和 16 位数据类型)和实现任务。 NA但是,现在可以通过使用屏蔽的 NumPy 类型(例如Int64Dtype 或 PyArrow 类型 ( ))来使用R语义ArrowDtype。 与 NumPy 的差异# 对于Series和DataFrame对象,var()通过 进行归一化 N-1以生成总体方差 的无偏估计,而 NumPy 的 numpy.var()归一化为 N,用于测量样本的方差。请注意, pandas 和 NumPy 中的cov()标准化都是通过。N-1 线程安全# pandas 不是 100% 线程安全的。已知问题与copy()方法有关。如果您要对线程之间共享的对象进行大量复制 DataFrame,我们建议在发生数据复制的线程内持有锁。 请参阅此链接 了解更多信息。 字节顺序问题# 有时,您可能需要处理在与运行 Python 的机器上字节顺序不同的机器上创建的数据。此问题的常见症状是出现如下错误: Traceback ... ValueError: Big-endian buffer not supported on little-endian compiler 要解决此问题,您应该将底层 NumPy 数组转换为本机系统字节顺序,然后再将其传递给Series或DataFrame 构造函数,使用类似于以下内容的内容: In [49]: x = np.array(list(range(10)), ">i4") # big endian In [50]: newx = x.byteswap().view(x.dtype.newbyteorder()) # force native byteorder In [51]: s = pd.Series(newx) 有关更多详细信息,请参阅有关字节顺序的 NumPy 文档。