Python:循环中IF语句处理不一致

3

我有一个名为df的数据框,其中包含条件和值。

import pandas as pd
df=pd.DataFrame({'COND':['X','X','X','Y','Y','Y'], 'VALUE':[1,2,3,1,2,3]})

因此,df 的外观如下:
  COND  VALUE
     X      1
     X      2
     X      3
     Y      1
     Y      2
     Y      3

我使用循环根据COND来对df进行子集划分,并编写单独的文本文件,包含每个条件的值。

conditions = {'X','Y'}
for condition in conditions:
    df2 = df[df['COND'].isin([condition])][['VALUE']]
    df2.to_csv(condition + '_values.txt', header=False, index=False)

最终结果是两个文本文件:X_vals.txt和Y_vals.txt,它们都包含1 2 3。截至目前为止,一切都按预期工作。
我想进一步对df进行子集筛选,只考虑一个条件。例如,也许我只希望从条件Y中获取所有值,但仅从条件X中获取小于3的值。在这种情况下,X_vals.txt应包含1 2,而Y_vals.txt应包含1 2 3。我尝试使用IF语句实现这一点:
conditions = {'X','Y'}
for condition in conditions:
    if condition == 'X':
        df = df[df['VALUE'] < 3]

    df2 = df[df['COND'].isin([condition])][['VALUE']]
    df2.to_csv(condition + '_values.txt', header=False, index=False)

这里出现了不一致的情况。上面的代码运行良好(即X_vals.txt包含1 2,Y_vals.txt包含1 2 3,正如预期的那样),但是当我使用if condition=='Y'而不是if condition=='X'时,它就会出错,两个文本文件都只包含1 2

换句话说,如果在IF语句中指定conditions的第一个元素,则它将按预期工作,然而如果指定第二个元素,则它将应用于来自两个条件的值的< 3子集。

这里发生了什么,我该如何解决呢?

谢谢!


df = df[df['VALUE'] < 3] 这样你在循环中改变了初始数据框 - 这是你的错误。 - Dmitriy K.
3个回答

7
你遇到的问题是由于你在循环中覆盖了df变量导致的。
conditions = {'X','Y'}
for condition in conditions:
    if condition == 'X':
        df = df[df['VALUE'] < 3]  # <-- HERE'S YOUR ISSUE

    df2 = df[df['COND'].isin([condition])][['VALUE']]
    df2.to_csv(condition + '_values.txt', header=False, index=False)

在遍历集合 conditions 时,首先会得到 condition = 'Y' ,然后是 condition = 'X' ,这有点让我惊讶。但是,由于集合是一种无序的集合(即它不声称具有其元素的固有顺序),所以这应该不太令人困扰:Python 只是以最内在方便的方式读取元素。
您可以使用 conditions = ['X','Y'] 来遍历列表(有序集合)。然后它将首先处理 X,然后是 Y。但是,如果您这样做,您将以相反的方式获得同样的错误(即if condition == 'Y'有效,但if condition == 'X'无效)。
这是因为在循环运行一次后,df 已被重新分配到仅包含小于三的原始 df 子集。这就是为什么如果if condition语句在循环的第一次通过中触发,则两个文件中仅获取值1和2的原因。
现在是修复方法:

conditions = ['X', 'Y']

for condition in conditions:

    csv_name = f"{condition}_values.txt"

    if condition == 'X':
        df_filter = f"VALUE < 3 & COND == '{condition}'"
    else:
        df_filter = f"COND == '{condition}'"

    df.query(df_filter).VALUE.to_csv(csv_name, header=False, index=False)

我介绍了DataFrame.query方法,通常比尝试创建用作掩码的布尔系列更简洁。

f-string语法仅在Python 3.6+上有效,如果您使用较低版本,则应相应修改(例如,df_filter =“COND == '{}'”。format(condition))。


谢谢您详细的回答!您能否请澄清如何使用f-string在每行中适应较低版本? - Lyam
这就是我在上一行示例中所说的意思。格式化字符串有多种方法,但其中一种方法是使用字符串的 format 方法。在每种情况下,只需删除 f 前缀并在字符串末尾加上 .format(condition=condition)。或者,为了简洁起见,可以使用 .format(condition) 并将 {condition} 更改为 {},就像我的示例一样。 - Paddy Alton

0
我们可以将条件写入dict,然后在groupby之前使用map过滤df。
cond = {'X' : 2, 'Y' : 3}
subdf = df[df['VALUE']<df.COND.map(cond)]
for x, y in subdf.groupby('COND'):
    y.to_csv(x + '_values.txt')

感谢提供这个解决方法,但我真的想了解现有代码的问题在哪里。我正在使用一个比我上面描述的更复杂的脚本,所以我需要知道这里出了什么问题。 - Lyam

0
df=pd.DataFrame({'COND':['X','X','X','Y','Y','Y'], 'VALUE':[1,2,3,1,2,3]})

conditions = df.COND

for condition in conditions:
 print(condition)
 df2=df[df['COND'].isin([condition])][['VALUE']]
 df2.to_csv(condition + '_values.txt',header=False, index=False)


for condition in conditions:
 if condition=='X':
     df=df[df['VALUE'] < 3]

 df2=df[df['COND'].isin([condition])][['VALUE']]
 df2.to_csv(condition + '_values.txt',header=False, index=False)

你没有指定变量“Condition”,所以它给了你一个错误。 尝试在for循环之前执行:

conditions = df.COND


糟糕 - 我最初将条件定义为conditions={'X','Y'}(已编辑以包括此内容),但是您的答案揭示了新问题。使用conditions={'X','Y'}定义条件会导致我描述的问题,但是像您建议的那样使用conditions=df.COND会导致代码每次都失败 - 也就是说,无论我在IF语句中使用哪个条件,都会对df进行子集化。有趣... - Lyam

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