【问题标题】:Why am I receiving this error when looping through pandas dataframe为什么在循环遍历 pandas 数据帧时会收到此错误
【发布时间】:2023-03-16 08:35:02
【问题描述】:

我正在尝试遍历数据框中的两列,并根据上述两个列值将10 添加到新列中。例如,如果 A 列 > B 列,则将 1 添加到 C 列。但是,我不断收到以下错误,我不知道为什么。

ValueError: The truth value of a Series is ambiguous. Use a.empty, 
a.bool(), a.item(), a.any() or a.all().

我的代码:

for i in df.itertuples():
    if df['AdjClose'] > df['30ma']:
        df['position'] = 1
elif df['AdjClose'] < df['30ma']:
    df['position'] = 0

【问题讨论】:

  • 下面的答案不是很好。你应该使用df['position'] = (df['AdjClose'] &gt; df['30ma']).astype(int)

标签: python python-3.x pandas loops


【解决方案1】:

您实际上并没有遍历行。在您的 if 语句中,您的条件不是TrueFalse,而是一个系列。因此,错误。一个系列没有真假,它是一个系列。编写代码的更正确方法是

for i in range(len(df)):
    if df.loc[i, 'AdjClose'] > df.loc[i, '30ma']:
        df.loc[i, 'position'] = 1
    elif df.loc[i, 'AdjClose'] < df.loc[i, '30ma']:
        df.loc[i, 'position'] = 0

一种更短、更简洁、更符合 Pandas 的方式来编写代码,同时还具有运行速度更快的优势:

df.loc[df.AdjClose > df['30ma'], 'position'] = 1
df.loc[df.AdjClose < df['30ma'], 'position'] = 0

我强烈建议阅读有关索引的文档,在 pandas 中开始可能会有点棘手。 https://pandas.pydata.org/pandas-docs/stable/indexing.html

编辑: 请注意,for 循环代码假设您的索引由从 0 到 n-1 的唯一值组成。如果你有不同的索引,它会有点复杂。见https://pandas.pydata.org/pandas-docs/stable/whatsnew.html#deprecate-ix

【讨论】:

  • 如果df.loc[i, 'AdjClose']==df.loc[i, '30ma'],此方法(如@NaN 的原始代码)将使'位置'未定义。但是,如果您想在这种情况下分配 0,则可以使用更简单的形式:df['position'] = (df.AdjClose &gt; df['30ma']).astype(int)。这会为表达式为真的所有行显示True,否则显示False。然后.astype(int) 将其转换为 0/1 值。
  • df.loc[] 需要索引元素,但您的第一个示例是发送整数偏移量。如果以其他方式对行进行索引,这将不起作用。你可以通过使用for i in df.index:...来解决这个问题。
  • Matthias,您对 df.loc 的看法是正确的,我在编辑中也提到了这一点。 for i in df.index 在重复索引的情况下不起作用。在实际循环中使用带有 iloc 的 for i in range(len(df)) 将更适用于所有索引。
  • Ian,关于重复索引的要点。通过循环处理这将是一个棘手的情况。
【解决方案2】:

您正在尝试针对多个值测试布尔值(类似于 if pd.Series([False, True, False]),但不清楚结果是什么),因此 pandas 会引发该错误。

该消息建议您可以使用any() 返回是否有任何值(在本例中是您正在测试的一个值)为True

所以可能是这样的:

for i in df.itertuples():
    if (df['AdjClose'] > df['30ma']).any():
        df['position'] = 1
    elif (df['AdjClose'] < df['30ma']).any():
        df['position'] = 0

有关更多详细信息,请参阅这些文档Using If/Truth Statements with pandas

【讨论】:

    【解决方案3】:

    您的代码正在调用df.itertuples,但没有使用结果。您可以使用 Ian Kent 的建议之一或类似的方法来解决此问题:

    for row in df[['AdjClose', '30ma']].itertuples():
        if row[1] > row[2]:  # note: row[0] is the index value
            df.loc[row.Index, 'position'] = 1
        elif row[1] < row[2]:
            df.loc[row.Index, 'position'] = 0
    

    如果您的列的名称都是有效的 Python 标识符,您可以使用更简洁的名称:

    for row in df.itertuples():
        if row.AdjClose > row.ma30:
            df.loc[row.Index, 'position'] = 1
        elif row.AdjClose < row.ma30:
            df.loc[row.Index, 'position'] = 0
    

    请注意,如果df 的索引具有重复值,则这些都不起作用。

    你也可以使用df.apply,像这样:

    def pos(row):
        if row['AdjClose'] > row['30ma']:
            return 1
        elif row['AdjClose'] > row['30ma']:
            return 0
        else:
            return pd.np.nan  # undefined?
    
    df['position'] = df.apply(pos)
    

    或者只是

    df['position'] = df.apply(lambda row: 1 if row['AdjClose'] > row['30ma'] else 0)
    

    即使索引具有重复值,这也应该有效。但是,您必须为每一行定义一个值,即使是 row['AdjClose'] == row['30ma'] 所在的行。

    总体而言,Ian Kent 的第二个建议可能会让您获得最佳效果。

    【讨论】:

      猜你喜欢
      • 2021-05-16
      • 2022-01-14
      • 1970-01-01
      • 2020-11-21
      • 2021-12-28
      • 1970-01-01
      • 2021-12-15
      • 2021-12-16
      • 2018-06-22
      相关资源
      最近更新 更多