【问题标题】:Apply Function on DataFrame Index在 DataFrame 索引上应用函数
【发布时间】:2013-11-30 06:39:25
【问题描述】:

在 Pandas DataFrame 的索引上应用函数的最佳方法是什么? 目前我正在使用这种详细的方法:

pd.DataFrame({"Month": df.reset_index().Date.apply(foo)})

其中Date 是索引的名称,foo 是我正在应用的函数的名称。

【问题讨论】:

  • df.index.map(foo) 工作吗?
  • 它“有效”,但它返回一个 numpy 数组而不是 Pandas 系列。
  • 你的最终目标是什么?您可以将数组传递给 DataFrame 构造函数。或者做类似pd.Series(df.index).apply(foo)
  • 这完全取决于函数是什么......
  • 如果您只想修改现有 DataFrame 的索引,则可以按照 @HYRY 进行操作 df.index = df.index.map(foo)

标签: python pandas indexing dataframe


【解决方案1】:

您可以使用其to_series() 方法转换索引,然后根据您的需要使用applymap

ret = df.index.map(foo)                # Returns pd.Index
ret = df.index.to_series().map(foo)    # Returns pd.Series
ret = df.index.to_series().apply(foo)  # Returns pd.Series

以上所有内容都可以直接分配给df的新列或现有列:

df["column"] = ret

为了完整起见:pd.Index.mappd.Series.mappd.Series.apply 都是按元素操作的。我经常使用map 来应用由dictspd.Series 表示的查找。 apply 更通用,因为您可以传递任何函数以及额外的 argskwargsapplymap 之间的区别在 this SO thread 中进一步讨论。我不知道为什么pd.Index.apply 被省略了。

【讨论】:

  • 感谢您的详细回复,第三个选项让我摆脱了困境。
【解决方案2】:

正如 HYRY 在 cmets 中已经建议的那样,Series.map 是这里的最佳选择。只需将索引设置为结果系列。

简单示例:

df = pd.DataFrame({'d': [1, 2, 3]}, index=['FOO', 'BAR', 'BAZ'])
df
        d
FOO     1
BAR     2
BAZ     3

df.index = df.index.map(str.lower)
df
        d
foo     1
bar     2
baz     3

索引 != 系列

正如@OP 指出的那样。 df.index.map(str.lower) 调用返回一个 numpy 数组。 这是因为数据帧索引 基于 numpy 数组,而不是 Series。

将索引变成系列的唯一方法是从中创建一个系列。

pd.Series(df.index.map(str.lower))

警告

Index 类现在是StringAccessorMixin 的子类,这意味着您可以按如下方式进行上述操作

df.index.str.lower()

这仍然产生一个索引对象,而不是一个系列。

【讨论】:

  • 使用多索引,如果你想在你的函数中使用这两个项目,你可以使用切片,例如x[0]x[1].
  • 短一点的方法df.index.map(str.lower)
  • @JohnGalt 感谢您指出这一点。它不仅更短,而且更快,因为 str.lower 是编译的 cython 函数,而我编写的 lambda 函数不是。
【解决方案3】:

很多答案都将索引作为数组返回,这会丢失有关索引名称等的信息(尽管您可以这样做 pd.Series(index.map(myfunc), name=index.name))。它也不适用于 MultiIndex。

我的工作方式是使用“重命名”:

mix = pd.MultiIndex.from_tuples([[1, 'hi'], [2, 'there'], [3, 'dude']], names=['num', 'name'])
data = np.random.randn(3)
df = pd.Series(data, index=mix)
print(df)
num  name 
1    hi       1.249914
2    there   -0.414358
3    dude     0.987852
dtype: float64

# Define a few dictionaries to denote the mapping
rename_dict = {i: i*100 for i in df.index.get_level_values('num')}
rename_dict.update({i: i+'_yeah!' for i in df.index.get_level_values('name')})
df = df.rename(index=rename_dict)
print(df)
num  name       
100  hi_yeah!       1.249914
200  there_yeah!   -0.414358
300  dude_yeah!     0.987852
dtype: float64

这样做的唯一技巧是您的索引需要具有不同的多索引级别的唯一标签,但也许比我更聪明的人知道如何解决这个问题。就我而言,这在 95% 的时间里都有效。

【讨论】:

    【解决方案4】:

    假设您想通过将函数“foo”应用于索引来在当前 DataFrame 中创建一列。你可以写...

    df['Month'] = df.index.map(foo)
    

    要单独生成系列,您可以改为...

    pd.Series({x: foo(x) for x in foo.index})
    

    【讨论】:

    • 强烈建议不要在 pandas/numpy echo-system 中使用 for 循环。它的内存效率非常低,并且在使用较大的数据集时很容易崩溃。
    猜你喜欢
    • 2020-05-11
    • 2012-12-07
    • 2022-11-15
    • 2017-11-14
    • 1970-01-01
    • 1970-01-01
    • 2022-11-14
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多