【问题标题】:How to create new columns based off specific conditions?如何根据特定条件创建新列?
【发布时间】:2019-10-03 17:33:15
【问题描述】:

我有一个多索引数据框。索引由 ID 和日期表示。我的 3 列是成本、收入和支出。

我想根据特定条件创建 3 个新列。

1) 我要创建的第一个新列将基于条件,对于每个 ID 的 3 个最近的日期,如果成本列持续减少,则将新行值标记为“NEG”,如果不是,则将其标记为“不”。

2) 我要创建的第二列将基于条件,对于最近的 3 个日期,如果收入列持续减少,则将新行值标记为“NEG”,否则将其标记为“不'。

3) 我要创建的第三列将基于条件,对于最近的 3 个日期,如果支出列持续增加,则将新行值标记为“POS”或保持相同的标签新行值为“STABLE”。

idx = pd.MultiIndex.from_product([['001', '002', '003','004'],
                              ['2017-06-30', '2017-12-31', '2018-06-30','2018-12-31','2019-06-30']],
                             names=['ID', 'Date'])
col = ['Cost', 'Revenue','Expenditure']

 dict2 = {'Cost':[12,6,-2,-10,-16,-10,14,12,6,7,4,2,1,4,-4,5,7,9,8,1],
     'Revenue':[14,13,2,1,-6,-10,14,12,6,7,4,2,1,4,-4,5,7,9,18,91],
     'Expenditure':[17,196,20,1,-6,-10,14,12,6,7,4,2,1,4,-4,5,7,9,18,18]}

df = pd.DataFrame(dict2,idx,col)

我曾尝试创建一个函数,然后将其应用于我的 DF,但我不断收到错误...

我想最终得到的解决方案看起来像这样..

idx = pd.MultiIndex.from_product([['001', '002', '003','004'],
                              ['2017-06-30', '2017-12-31', '2018-06-30','2018-12-31','2019-06-30']],
                             names=['ID', 'Date'])
col = ['Cost', 'Revenue','Expenditure', 'Cost Outlook', 'Revenue Outlook', 'Expenditure Outlook']

dict3= {'Cost':  [12,6,-2,-10,-16,
            -10,14,12,6,7,
            4,2,1,4,-4,
            5,7,9,8,1],


    'Cost Outlook':   ['no','no','NEG','NEG','NEG', 
                       'no','no','no','NEG','NEG', 
                       'no','no','NEG','no','no', 
                       'no','no','no','no','NEG'],



    'Revenue':[14,13,2,1,-6,
               -10,14,12,6,7,
               4,2,1,4,-4,
               5,7,9,18,91],

    'Revenue Outlook': ['no','no','NEG','NEG','NEG', 
                        'no','no','no','NEG','NEG', 
                        'no','no','NEG','no','no', 
                        'no','no','no','no','no'],



    'Expenditure':[17,196,1220,1220, -6,
                   -10,14,120,126,129, 
                   4,2,1,4,-4,
                   5,7,9,18,18],


    'Expenditure Outlook':['no','no','POS','POS','no', 
                           'no','no','POS','POS','POS', 
                           'no','no','no','no','no', 
                           'no','no','POS','POS','STABLE']
   }

df_new  = pd.DataFrame(dict3,idx,col)

【问题讨论】:

  • 当最近 3 个月为 196, 1220, 1220 时,2018-12-31 中的 Expenditure 如何稳定?你说基于 3 个月,但只有最近 2 个月是相等的。
  • 你说得对,让我编辑

标签: python-3.x pandas dataframe multi-index


【解决方案1】:

我会这样做:

# update Cost and Revenue Outlooks 
# because they have similar conditions
for col in ['Cost', 'Revenue']:
    groups = df.groupby('ID')

    outlook = f'{col} Outlook'
    df[outlook] = groups[col].diff().lt(0)

    # moved here
    df[outlook] = np.where(groups[outlook].rolling(2).sum().eq(2), 'NEG', 'no')

# update Expenditure Outlook
col = 'Expenditure'
outlook = f'{col} Outlook'
s = df.groupby('ID')[col].diff()

df[outlook] = np.select( (s.eq(0).groupby(level=0).rolling(2).sum().eq(2),
                          s.gt(0).groupby(level=0).rolling(2).sum().eq(2)),
                        ('STABLE', 'POS'), 'no')

【讨论】:

  • 支出开始的地方应该有一个缩进吗?我也收到了 Outlook= 语法错误的错误
  • 也不会对所有ID重复它,只有最后一个,有什么解决办法吗?
  • 我搞砸了不同单元格的复制/粘贴。查看更新。
  • 它仍然只对 1 个 ID 执行此操作,有没有办法确保它针对每个 ID 进行迭代?
  • 你说的一个ID是什么意思? groupby 的重点是一次处理所有 id。你检查输出了吗?
【解决方案2】:

看看这是否有效:

is_descending = lambda a: np.all(a[:-1] > a[1:])
is_ascending = lambda a: np.all(a[:-1] <= a[1:])
df1 = df.reset_index()
df1["CostOutlook"] = df1.groupby("ID").Cost.rolling(3).apply(is_descending).fillna(0).apply(lambda x: "NEG" if x > 0 else "no").to_list()
df1["RevenueOutlook"] = df1.groupby("ID").Revenue.rolling(3).apply(is_descending).fillna(0).apply(lambda x: "NEG" if x > 0 else "no").to_list()
df1["ExpenditureOutlook"] = df1.groupby("ID").Expenditure.rolling(3).apply(is_ascending).fillna(0).apply(lambda x: "POS" if x > 0 else "no").to_list()
df1 = df1.set_index(["ID", "Date"])

注意:不处理“STABLE”的要求。

编辑: 这是替代解决方案:

is_descending = lambda a: np.all(a[:-1] > a[1:])
def is_ascending(a):
    if np.all(a[:-1] <= a[1:]):
        if a[-1] == a[-2]:
            return 2
        return 1
    return 0

for col in ['Cost', 'Revenue']:
    outlook = df[col].unstack(level="ID").rolling(3).apply(is_descending).fillna(0).replace({0.0:"no", 1.0:"NEG"}).unstack().rename(f"{col} outlook")
    df = df.join(outlook)

col = "Expenditure"
outlook = df[col].unstack(level="ID").rolling(3).apply(is_ascending).fillna(0).replace({0.0:"no", 1.0:"POS", 2.0:"STABLE"}).unstack().rename(f"{col} outlook")
df = df.join(outlook)

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-10-02
    • 2022-11-12
    • 2021-08-27
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多