为什么我在对Pandas DataFrame进行 iterrows 后,这个函数没有“生效”?

3
我有一个时间戳温度和风速数值的DataFrame,以及一个将它们转换为“风寒”的函数。我使用iterrows在每一行上运行该函数,并希望得到一个具有不错“风寒”列的DataFrame。
然而,虽然它似乎在执行过程中可以工作,并且至少已经“工作”了一次,但我似乎无法始终复制它。我感觉这与DataFrames的结构有关,但我希望有人能帮助解决。
In [28]: bigdf.head()
Out[28]: 


                           Day  Temperature  Wind Speed  Year
2003-03-01 06:00:00-05:00  1    30.27        5.27        2003
2003-03-01 07:00:00-05:00  1    30.21        4.83        2003
2003-03-01 08:00:00-05:00  1    31.81        6.09        2003
2003-03-01 09:00:00-05:00  1    34.04        6.61        2003
2003-03-01 10:00:00-05:00  1    35.31        6.97        2003

所以我在bigdf中添加了一个“风寒”列,并预填了NaN
In [29]: bigdf['Wind Chill'] = NaN

然后我尝试迭代遍历行,添加实际的风寒效应。

In [30]: for row_index, row in bigdf[:5].iterrows():
    ...:     row['Wind Chill'] = windchill(row['Temperature'], row['Wind Speed'])
    ...:     print row['Wind Chill']
    ...:
24.7945889994
25.1365267133
25.934114012
28.2194307516
29.5051046953

正如你所看到的,新值似乎已应用于“风寒”列。以下是windchill函数,以防有帮助:

def windchill(temp, wind):
    if temp>50 or wind<=3:
        return temp
    else:
        return 35.74 + 0.6215*temp - 35.75*wind**0.16 + 0.4275*temp*wind**0.16

但是,当我再次查看DataFrame时,NaN仍然存在:

In [31]: bigdf.head()
Out[31]: 

                           Day  Temperature  Wind Speed  Year  Wind Chill
2003-03-01 06:00:00-05:00  1    30.27        5.27        2003  NaN
2003-03-01 07:00:00-05:00  1    30.21        4.83        2003  NaN
2003-03-01 08:00:00-05:00  1    31.81        6.09        2003  NaN
2003-03-01 09:00:00-05:00  1    34.04        6.61        2003  NaN
2003-03-01 10:00:00-05:00  1    35.31        6.97        2003  NaN

更奇怪的是,它曾经过几次成功的经历,我无法确定自己到底做错了什么。
我必须承认,我对pandas的内部工作机制并不是特别熟悉,而且在索引等方面容易混淆,所以我觉得我可能在这里缺少了非常基本的东西(或者用了更难的方式)。
谢谢!
3个回答

9
您可以使用apply来实现此功能:
In [11]: df.apply(lambda row: windchill(row['Temperature'], row['Wind Speed']),
                 axis=1)
Out[11]:
2003-03-01 06:00:00-05:00    24.794589
2003-03-01 07:00:00-05:00    25.136527
2003-03-01 08:00:00-05:00    25.934114
2003-03-01 09:00:00-05:00    28.219431
2003-03-01 10:00:00-05:00    29.505105

In [12]: df['Wind Chill'] = df.apply(lambda row: windchill(row['Temperature'], row['Wind Speed']),
                                    axis=1)

In [13]: df
Out[13]:
                           Day  Temperature  Wind Speed  Year  Wind Chill
2003-03-01 06:00:00-05:00    1        30.27        5.27  2003   24.794589
2003-03-01 07:00:00-05:00    1        30.21        4.83  2003   25.136527
2003-03-01 08:00:00-05:00    1        31.81        6.09  2003   25.934114
2003-03-01 09:00:00-05:00    1        34.04        6.61  2003   28.219431
2003-03-01 10:00:00-05:00    1        35.31        6.97  2003   29.505105

为了解释您的困惑,我认为它源于行变量是df的副本而不是视图,因此更改不会传播:

In [21]: for _, row in df.iterrows(): row['Day'] = 2

我们看到它已成功地将更改应用于副本中的row变量。
In [22]: row
Out[22]:
Day               2.00
Temperature      35.31
Wind Speed        6.97
Year           2003.00
Name: 2003-03-01 10:00:00-05:00

但它们没有更新到DataFrame:
In [23]: df
Out[23]:
                           Day  Temperature  Wind Speed  Year
2003-03-01 06:00:00-05:00    1        30.27        5.27  2003
2003-03-01 07:00:00-05:00    1        30.21        4.83  2003
2003-03-01 08:00:00-05:00    1        31.81        6.09  2003
2003-03-01 09:00:00-05:00    1        34.04        6.61  2003
2003-03-01 10:00:00-05:00    1        35.31        6.97  2003

以下操作不会改变df的值:
In [24]: row = df.ix[0]  # also a copy

In [25]: row['Day'] = 2

如果我们采取一种观点:(我们会看到一个变化 df。)

In [26]: row = df.ix[2:3]  # this one's a view

In [27]: row['Day'] = 3

请查看返回视图与返回副本(文档中)


我怀疑这与副本和视图有关,但我一直在反着想而让自己非常困惑。感谢您详细的答案! - wimsy
我曾经遇到过类似的问题,采用了相似的解决方案,但是这里有个奇怪的地方:它在旧版本上以某种方式起作用,但是在其他机器上使用较新版本的 Pandas 时却不起作用。这真的让我抓狂。因此,如果有人在类似的问题上开始拔光头发,我想我可以把这个解决方案传递给他们。 - ViennaMike
@ViennaMike,你是在说上面的代码适用于新版还是旧版的pandas?在pandas的apply函数中有一些边缘情况在最近几个版本中进行了调整,所以这可能是其中之一! - Andy Hayden
@AndyHayden,是的,我使用的是旧版本的Anaconda,其中包含Pandas的11版,我使用iterrows,并且我编码的方式可以正常工作,通过行引用对原始数据帧进行更新。但是当我尝试在两个较新的版本上运行时,这种方法不起作用(显然行引用了副本而不是原始数据)。在我的代码中使用直接的.loc引用来修复它,引用原始数据帧。 - ViennaMike

1
尝试使用以下内容:
bigdf['Wind Chill'] = bigdf.apply(lambda x: windchill(x['Temperature'], x['Wind Speed']), axis=1)

使用您的简单 windchill 函数一次性为整个 DataFrame 进行计算。


1
我认为您不需要任何显式循环。以下内容应该能够满足您的需求。
bigdf = pd.DataFrame({'Temperature': [30.27, 30.21, 31.81], 'Wind Speed': [5.27, 4.83, 6.09]})

def windchill(temp, wind):
    "compute the wind chill given two pandas series temp and wind"
    tomodify = (temp<=50) & (wind>3) #check which values need to be modified
    t = temp.copy()  #create a new series
    # change only the values that need modification
    t[tomodify] = 35.74 + 0.6215*temp[tomodify] - 35.75*wind[tomodify]**0.16 +
        0.4275*temp[tomodify]*wind[tomodify]**0.16
    return t

bigdf['Wind Chill'] = windchill(bigdf['Temperature'], bigdf['Wind Speed'])

bigdf

   Temperature  Wind Speed  Wind Chill
0        30.27        5.27   24.794589
1        30.21        4.83   25.136527
2        31.81        6.09   25.934114

提示:这个windchill的实现也适用于numpy数组。


谢谢。我通过谷歌搜索发现修改“风寒温度”是另一个选项,但我真的很想弄清楚我在原来的方法中做错了什么。 :) - wimsy
知道了。很好,你找到了解释。 - Francesco Montesano
我曾经遇到过类似的问题,采用了相似的解决方案,但是这里有个奇怪的地方:它在旧版本上以某种方式起作用,但是在其他机器上使用更新版本的 Pandas 时却不行。这真的让我抓狂。因此,如果有人遇到类似的问题开始拔光头发,我想把这个解决方案分享给大家。 - ViennaMike

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接