【问题标题】:Apply a lambda with a shift function in python pandas were some null elements are to be replaced在 python pandas 中应用带有 shift 函数的 lambda 来替换一些空元素
【发布时间】:2015-01-19 15:11:04
【问题描述】:

我正在尝试在数据框中执行以下操作。 如果 Period 不是 1,则更改列损耗值,然后将该行中保留列的值乘以 groupby 中上一行中的损耗值。我的尝试如下:

import pandas as pd

data = {'Country': ['DE', 'DE', 'DE', 'US', 'US', 'US', 'FR', 'FR', 'FR'],
    'Week': ['201426', '201426', '201426', '201426', '201425', '201425', '201426', '201426', '201426'],
    'Period': [1, 2, 3, 1, 1, 2, 1, 2, 3],
    'Attrition': [0.5,'' ,'' ,0.85 ,0.865,'' ,0.74 ,'','' ],
    'Retention': [0.95,0.85,0.94,0.85,0.97,0.93,0.97,0.93,0.94]}

df = pd.DataFrame(data, columns= ['Country', 'Week', 'Period', 'Attrition','Retention'])
print df

给我这个输出:

  Country    Week  Period Attrition  Retention
0      DE  201426       1       0.5       0.95
1      DE  201426       2                 0.85
2      DE  201426       3                 0.94
3      US  201426       1      0.85       0.85
4      US  201425       1     0.865       0.97
5      US  201425       2                 0.93
6      FR  201426       1      0.74       0.97
7      FR  201426       2                 0.93
8      FR  201426       3                 0.94

以下:

df['Attrition'] = df.groupby(['Country','Week']).apply(lambda x: x.Attrition.shift(1)*x['Retention'] if x.Period != 1 else x.Attrition)

print df

给我这个错误:

df['Attrition'] = df.groupby(['Country','Week']).apply(lambda x: x.Attrition.shift(1)*x['Retention'] if x.Period != 1 else x.Attrition)

ValueError:具有多个元素的数组的真值不明确。使用 a.any() 或 a.all()

更新:完整的编译解决方案

下面是我所追求的完整工作解决方案,基本上是使用 Primer 的答案,但添加了一个 while 循环以继续在数据框列上运行 Lambda 函数,直到没有更多的 NaN。

import pandas as pd
import numpy as np

data = {'Country': ['DE', 'DE', 'DE', 'US', 'US', 'US', 'FR', 'FR', 'FR'],
    'Week': ['201426', '201426', '201426', '201426', '201425', '201425', '201426', '201426', '201426'],
    'Period': [1, 2, 3, 1, 1, 2, 1, 2, 3],
    'Attrition': [0.5, '' ,'' ,0.85 ,0.865,'' ,0.74 ,'','' ],
    'Retention': [0.95,0.85,0.94,0.85,0.97,0.93,0.97,0.93,0.94]}

df = pd.DataFrame(data, columns= ['Country', 'Week', 'Period', 'Attrition','Retention'])
print df

输出:开始 DF

  Country    Week  Period Attrition  Retention
0      DE  201426       1       0.5       0.95
1      DE  201426       2                 0.85
2      DE  201426       3                 0.94
3      US  201426       1      0.85       0.85
4      US  201425       1     0.865       0.97
5      US  201425       2                 0.93
6      FR  201426       1      0.74       0.97
7      FR  201426       2                 0.93
8      FR  201426       3                 0.94

解决方案:

#Replaces empty string with NaNs
df['Attrition'] = df['Attrition'].replace('', np.nan)

#Stores a count of the number of null or NaNs in the column.
ContainsNaN = df['Attrition'].isnull().sum()

#run the loop while there are some NaNs in the column.
while ContainsNaN > 0:    
    df['Attrition'] = df.groupby(['Country','Week']).apply(lambda x: pd.Series(np.where((x.Period != 1), x.Attrition.shift() * x['Retention'], x.Attrition)))        
    ContainsNaN = df['Attrition'].isnull().sum()

print df

输出:结果

  Country    Week  Period Attrition  Retention
0      DE  201426       1       0.5       0.95
1      DE  201426       2     0.425       0.85
2      DE  201426       3    0.3995       0.94
3      US  201426       1      0.85       0.85
4      US  201425       1     0.865       0.97
5      US  201425       2   0.80445       0.93
6      FR  201426       1      0.74       0.97
7      FR  201426       2    0.6882       0.93
8      FR  201426       3  0.646908       0.94

【问题讨论】:

  • 其实,只是作为这个的后续。上面的代码工作正常,问题解决了。然后我将它调整为我更复杂的问题,我开始收到错误,现在即使上面的代码也没有运行。这是我在第 22 行遇到的错误。“引发 TypeError('插入列的不兼容索引'我的 pandas 版本是 0.15.2 我有 0.12 但读到有一些问题,所以升级到 0.15.2 但它没有解决问题。 TypeError: incompatible index of inserted column with frame index" 我还使用 Python 2.7.5 | Anaconda 1.8.0(64 位)和 Spyder 接口。

标签: python pandas lambda


【解决方案1】:

首先,您的Attrition 列将数字数据与空字符串'' 混合在一起,这通常不是一个好主意,应该在尝试对此列进行计算之前修复:

df.loc[df['Attrition'] == '', 'Attrition'] = pd.np.nan
df['Attrition'] = df.Attrition.astype('float')

你得到的错误是因为你在.apply:x.Period != 1 中的条件产生了一个布尔数组:

0    False
1     True
2     True
3    False
4    False
5     True
6    False
7     True
8     True
Name: Period, dtype: bool

哪个.apply 不知道如何处理,因为它的模棱两可(即在这种情况下应该是什么?)。

您可以考虑使用numpy.where 完成此任务:

import numpy as np
g = df.groupby(['Country','Week'], as_index=0, group_keys=0)
df['Attrition'] = g.apply(lambda x: pd.Series(np.where((x.Period != 1), x.Attrition.shift() * x['Retention'], x.Attrition)).fillna(method='ffill')).values
df

屈服:

  Country    Week  Period  Attrition  Retention
0      DE  201426       1      0.500       0.95
1      DE  201426       2      0.425       0.85
2      DE  201426       3      0.425       0.94
3      US  201426       1      0.740       0.85
4      US  201425       1      0.688       0.97
5      US  201425       2      0.688       0.93
6      FR  201426       1      0.865       0.97
7      FR  201426       2      0.804       0.93
8      FR  201426       3      0.850       0.94

请注意,我添加了.fillna 方法,它用最后观察到的值填充NaN

【讨论】:

  • 谢谢 Primer,我只是一一浏览您的回复。感谢您用空字符串澄清问题。我不太明白这条线是如何工作的 'df.loc[df['Attrition'] == '', 'Attrition'] = pd.np.nan' 我设法找到了一个对我来说更简单的例子使用 ie 'df['Attrition'] = df['Attrition'].replace('', np.nan)' 这似乎也有效。你觉得这个方法有什么问题吗?
  • x.Period != 1 Boolean 现在对我来说是有意义的,谢谢。
  • 对于最后一部分,在第 2 行第 3 行中,我希望随后也可以用相同的公式填充,即第 2 行(0.425)x 0.94。我猜我需要遍历每一行或重复 lambda 直到没有更多的 NaN?我假设一个 lambda 函数会以一种 iterrows 方式自动应用该函数。
  • 我认为在这个例子中使用.replace 填写NaNs 没有问题。在应用所需的计算后,您最初的问题没有指定任何关于填充 NaNs 的内容,因此没有做任何事情。为了解决这个问题,我稍微修改了答案——在.apply 部分添加.fillna() 方法)。希望这能回答问题。
  • 实际上我不想填充 NaN,我希望每个 Period 都应用 Attrition.shift(1) x Retention 公式,但第一个周期除外。您的原始回复仅用计算填充了第一个 NaN 或 Empty 字符串。实际上,在我的真实数据中,每个 groupby 中有 52 个句点,我需要所有这些句点来应用计算。我认为这个 lambda 虽然在这种情况下不起作用,除非我通过 groupby 行。在我上面的评论中,我的意思是我希望第 2 行中的第 3 周期为 0.435 x 0.94。不是第 2 期的填充。
猜你喜欢
  • 1970-01-01
  • 2021-05-27
  • 2022-01-27
  • 1970-01-01
  • 1970-01-01
  • 2018-02-23
  • 1970-01-01
  • 2018-08-05
  • 2016-11-17
相关资源
最近更新 更多