python - 为什么需要将 map 类型转换为列表以将其分配给 Pandas 系列?

标签 python python-3.x pandas

我刚刚开始学习 pandas 的基础知识,有一件事让我思考。

import pandas as pd
data = pd.DataFrame({'Column1': ['A', 'B', 'C']})
data['Column2'] = map(str.lower, data['Column1'])
print(data)

这个程序的输出是:

   Column1                             Column2
 0       A  <map object at 0x00000205D80BCF98>
 1       B  <map object at 0x00000205D80BCF98>
 2       C  <map object at 0x00000205D80BCF98>

获得所需输出的一种可能解决方案是将 map 对象类型转换为列表。

import pandas as pd
data = pd.DataFrame({'Column1': ['A', 'B', 'C']})
data['Column2'] = list(map(str.lower, data['Column1']))
print(data)

输出:

   Column1 Column2
 0       A       a
 1       B       b
 2       C       c

但是,如果我使用 range(),它在 Python 3 中也返回自己的类型,则无需将对象类型转换为列表。

import pandas as pd
data = pd.DataFrame({'Column1': ['A', 'B', 'C']})
data['Column2'] = range(3)
print(data)

输出:

   Column1  Column2
 0       A        0
 1       B        1
 2       C        2

为什么范围对象不需要类型转换而 map 对象需要类型转换?

最佳答案

TL;DR: range__getitem____len__,而 map没有。


详情

我假设创建新数据框列的语法在某种程度上是 Pandas.DataFrame.insert 的语法糖,它以 value a

作为参数

scalar, Series, or array-like

鉴于此,问题似乎简化为“为什么 pandas 将列表和范围视为类数组,而不是 map ?”

参见:numpy: formal definition of "array_like" objects? .

如果您尝试创建超出范围的数组,它可以正常工作,因为范围足够接近类似数组,但您不能使用映射来这样做。

>>> import numpy as np
>>> foo = np.array(range(10))
>>> bar = np.array(map(lambda x: x + 1, range(10))
>>> foo
array([0, 1, 2, 3, 4, 5, 6, 7, 8, 9])
>>> bar
array(<map object at 0x7f7e553219e8>, dtype=object)

map 不是“类数组”,而 range 是。

进一步研究 PyArray_GetArrayParamsFromObject ,在链接的答案中提到,函数调用 PySequence_Check 的结尾。该代码是 python 代码,在 Stack Overflow 上有很好的讨论:What is Python's sequence protocol? .

早些时候,在 same file ,它说:

   /*
     * PySequence_Check detects whether an old type object is a
     * sequence by the presence of the __getitem__ attribute, and
     * for new type objects that aren't dictionaries by the
     * presence of the __len__ attribute as well. In either case it
     * is possible to have an object that tests as a sequence but
     * doesn't behave as a sequence and consequently, the
     * PySequence_GetItem call can fail. When that happens and the
     * object looks like a dictionary, we truncate the dimensions
     * and set the object creation flag, otherwise we pass the
     * error back up the call chain.
     */

这似乎是“类数组”的主要部分 - 任何具有 getitemlen 的项都是类数组。 range 两者都有,而 map 两者都没有。

自己试试吧!

__getitem____len__ 是生成序列所必需且充分的,因此可以让列按您的意愿显示,而不是作为单个对象显示。

试试这个:

class Column(object):
    def __len__(self):
        return 5
    def __getitem__(self, index):
        if 0 <= index < 5:
            return index+5
        else:
            raise IndexError

col = Column()
a_col = np.array(col)
  • 如果您没有__getitem__()__len()__,numpy 将为您创建一个数组,但它会包含其中的对象,它不会为您遍历。
  • 如果您同时拥有这两个功能,它会以您想要的方式显示。

(感谢 user2357112 纠正我。在一个稍微简单的例子中,我认为 __iter__ 是必需的。它不是。__getitem__ 函数确实需要确保索引不过在范围内。)

关于python - 为什么需要将 map 类型转换为列表以将其分配给 Pandas 系列?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/46732436/

相关文章:

python - Pandas 转出独特的专栏

python - 可能金额的功能不会超过一次

python - 为单词python创建一个嵌套字典

python - 类型错误 : 'float' object is not iterable with apply lambda

python - 如何使用单独的值范围对 pandas 数据框中的值进行分位数

python - 如何在新数据框中剪切并粘贴同一列作为下一列?

python - 在 sagemath 中定义函数的不同方式

python - Raspberry Pi 3B 上的 pymodbus 和电表连接错误

python - 从缺少 '_ctypes' 的源代码构建 Python3.7.3

python - Pandas 中仅针对所有先前行的条件运行计数