【问题标题】:How to manipulate data cell by cell in pandas df?如何在 pandas df 中逐个单元格地操作数据?
【发布时间】:2021-05-30 23:40:10
【问题描述】:

设样本df(df1)为,

我们可以通过以下方式操作df1的数据来实现df2或最终数据帧,

第 1 步:删除所有正数,包括零

在第 1 步之后,示例数据应如下所示,

第二步:如果column4A行为负数,column4B为空,则去掉column4A行的-ve数

第三步:如果column4A行为空,column4B为负数,则保留column4B行的-ve数

步骤 1,2 和 3 完成后,

第四步:如果column4的A和B都是负数,那么,

对于column4 的每个 A 和 B 行,检查 column4 的同一 A 和 B 行的左侧 (LHS) 值(对于给定月份)

步骤 4.1:如果 A 或 B 的 LHS 值中的任何一个是 -ve 数,则删除 B column4 的当前行值并保留 A @987654343 的当前行值@

在步骤 4.1 之后,示例数据应如下所示,

步骤 4.2

如果A和Bcolumn4的LHS值为空,则保留Bcolumn4的当前行值,删除Acolumn4的当前行值

步骤 4.2 之后的示例数据应如下所示,

由于我们仍然看到两个负数,我们再次执行步骤 4.1,然后最终的数据帧或 df2 将看起来像,

如何使用 pandas 实现上述目标?我能够实现到第 1 步,但不知道如何进一步进行。任何帮助将不胜感激。

这是我采取的方法,

import pandas as pd
df = pd.read_excel('df1.xlsx', engine='openpyxl')
df.to_pickle('./df1.pkl')
unpickled_df = pd.read_pickle('./df1.pkl')
rem_cols = ['column2', 'column3', 'column5', 'column6', 'column7']
unpickled_df['g'] = unpickled_df.groupby(['column1', 'column4'] ).cumcount()
df1 = unpickled_df.drop(rem_cols, axis=1)
df1 = df1.set_index(['column1','g', 'column4'])
df1.columns = pd.to_datetime(df1.columns, format='%b-%y').strftime('%b-%y')
first_date = df1.columns[0]
df1 = df1.unstack(-1)
df1 = df1.mask(df1.ge(0))
m1 = (df1.xs('A', level=1, axis=1, drop_level=False).notna() & 
      df1.xs('B', level=1, axis=1, drop_level=False).rename(columns={'B':'A'}, level=1).isna())
m2 = (df1.xs('B', level=1, axis=1, drop_level=False).notna() &
      df1.xs('A', level=1, axis=1, drop_level=False).rename(columns={'A':'B'}, level=1).isna())

m = m1.join(m2)
df1 = df1.mask(m)
df2 = df1.groupby(level=1, axis=1).shift(1, axis=1)
mask1 = df1.notna() & df2.isna() & (df1.columns.get_level_values(1) == 'A')[ None, :]
mask1[first_date] = False
mask2 = df1.notna() & df2.notna() & (df1.columns.get_level_values(1) == 'B')[ None, :]
df1 = df1.mask(mask1).mask(mask2).stack(dropna=False)
unpickled_df = unpickled_df[rem_cols + ['column1','g', 'column4']].join(df1, on=['column1','g', 'column4'])
#print(unpickled_df)

小测试数据: df1,

{'column1': ['ABC', 'ABC', 'CDF', 'CDF'], 'column4': ['A', 'B', 'A', 'B'], 'Feb-21': [0, 10, 0, 0], 'Mar-21': [0, 0, 70, 70], 'Apr-21': [-10, -10, -8, 60], 'May-21': [-30, -60, -10, 40], 'Jun-21': [-20, 9, -40, -20], 'Jul-21': [30, -10, 0, -20], 'Aug-21': [-30, -20, 0, -20], 'Sep-21': [0, -15, 0, -20], 'Oct-21': [0, -15, 0, -20]}

df2(预期输出),

{'column1': ['ABC', 'ABC', 'CDF', 'CDF'], 'column4': ['A', 'B', 'A', 'B'], 'Feb-21': [nan, nan, nan, nan], 'Mar-21': [nan, nan, nan, nan], 'Apr-21': [nan, -10.0, nan, nan], 'May-21': [-30.0, nan, nan, nan], 'Jun-21': [nan, nan, nan, -20.0], 'Jul-21': [nan, -10.0, nan, -20.0], 'Aug-21': [-30.0, nan, nan, -20.0], 'Sep-21': [nan, -15.0, nan, -20.0], 'Oct-21': [nan, -15.0, nan, -20.0]}

测试数据:

df1

{'column1': ['CT', 'CT', 'NBB', 'NBB', 'CT', 'CT', 'NBB', 'NBB', 'HHH', 'HHH', 'TP1', 'TP1', 'TPR', 'TPR', 'PP1', 'PP1', 'PP1', 'PP1'], 'column2': ['POUPOU', 'POUPOU', 'PRPRP', 'PRPRP', 'STDD', 'STDD', 'STDD', 'STDD', 'STEVT', 'STEVT', 'SYSYS', 'SYSYS', 'SYSYS', 'SYSYS', 'SHW', 'SHW', 'JV', 'JV'], 'column3': ['V', 'CV', 'V', 'CV', 'V', 'CV', 'V', 'CV', 'V', 'CV', 'V', 'CV', 'V', 'CV', 'V', 'CV', 'V', 'CV'], 'column4': ['A', 'B', 'A', 'B', 'A', 'B', 'A', 'B', 'A', 'B', 'A', 'B', 'A', 'B', 'A', 'B', 'A', 'B'], 'column5': [nan, nan, nan, nan, nan, nan, nan, nan, nan, nan, nan, nan, nan, nan, nan, nan, nan, nan], 'column6': ['BBB', 'BBB', 'CCC', 'CCC', 'BBB', 'BBB', 'BBB', 'BBB', 'VVV', 'VVV', 'CHCH', 'CHCH', 'CHCH', 'CHCH', 'CCC', 'CCC', 'CHCH', 'CHCH'], 'column7': ['Apr-21', 'Apr-21', 'Apr-21', 'Apr-21', 'Apr-21', 'Apr-21', 'Apr-21', 'Apr-21', 'Mar-21', 'Mar-21', 'Mar-21', 'Mar-21', 'Mar-21', 'Mar-21', 'Apr-21', 'Apr-21', 'Mar-21', 'Mar-21'], 'Feb-21': [11655, 0, 0, 0, 121117, 0, 14948, 0, 0, 0, 0, 0, 0, 0, 1838, 0, 0, 0], 'Mar-21': [0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, -16474.0, -16474.0, 7000.0, 7000.0, -19946.0, -19946.0, 16084.44444444444, 0.0, 0.0, 0.0], 'Apr-21': [104815.0, 104815.0, 17949.0, 17949.0, 96132.0, 96132.0, 0.0, 0.0, -17001.0, -33475.0, -878.0, 6122.0, 8398.0, -11548.0, -5297.073170731703, -5297.073170731703, -282.0, -282.0], 'May-21': [78260.0, 183075.0, 42557.0, 60506.0, -15265.0, 80867.0, -18.0, -18.0, 21084.0, -12391.0, -1831.0, 4291.0, 2862.0, -8686.0, 5261.25, -35.8231707317027, -369.0, -651.0], 'Jun-21': [-52480.0, 130595.0, -13258.0, 47248.0, -35577.0, 45290.0, 2434.0, 2416.0, 31147.0, 18756.0, -4310.0, -19.0, -4750.0, -13436.0, -92.0, -127.8231707317027, -280.0, -931.0], 'Jul-21': [-174544.0, -43949.0, -38127.0, 9121.0, -124986.0, -79696.0, -9707.0, -7291.0, 13577.0, 32333.0, 0.0, -19.0, -15746.0, -29182.0, 93.0, -34.8231707317027, -319.0, -1250.0], 'Aug-21': [35498.0, -8451.0, -37094.0, -27973.0, 79021.0, -675.0, -1423.0, -8714.0, 32168.0, 64501.0, 0.0, -19.0, 18702.0, -10480.0, 4347.634146341465, 4312.810975609762, -341.0, -1591.0], 'Sep-21': [44195.0, 35744.0, 2039.0, -25934.0, 70959.0, 70284.0, 2816.0, -5898.0, 38359.0, 102860.0, 0.0, -19.0, 18119.0, 7639.0, 5302.222222222219, 9615.033197831981, 0.0, -1591.0], 'Oct-21': [-13163.0, 22581.0, -4773.0, -30707.0, 205080.0, 275364.0, -709.0, -6607.0, -1397.0, 101463.0, 0.0, -19.0, 0.0, 7639.0, -34.0, 9581.033197831981, 0.0, -1591.0]}

df2(预期输出),

{'column1': ['CT', 'CT', 'NBB', 'NBB', 'CT', 'CT', 'NBB', 'NBB', 'HHH', 'HHH', 'TP1', 'TP1', 'TPR', 'TPR', 'PP1', 'PP1', 'PP1', 'PP1'], 'column2': ['POUPOU', 'POUPOU', 'PRPRP', 'PRPRP', 'STDD', 'STDD', 'STDD', 'STDD', 'STEVT', 'STEVT', 'SYSYS', 'SYSYS', 'SYSYS', 'SYSYS', 'SHW', 'SHW', 'JV', 'JV'], 'column3': ['V', 'CV', 'V', 'CV', 'V', 'CV', 'V', 'CV', 'V', 'CV', 'V', 'CV', 'V', 'CV', 'V', 'CV', 'V', 'CV'], 'column4': ['A', 'B', 'A', 'B', 'A', 'B', 'A', 'B', 'A', 'B', 'A', 'B', 'A', 'B', 'A', 'B', 'A', 'B'], 'column5': [nan, nan, nan, nan, nan, nan, nan, nan, nan, nan, nan, nan, nan, nan, nan, nan, nan, nan], 'column6': ['BBB', 'BBB', 'CCC', 'CCC', 'BBB', 'BBB', 'BBB', 'BBB', 'VVV', 'VVV', 'CHCH', 'CHCH', 'CHCH', 'CHCH', 'CCC', 'CCC', 'CHCH', 'CHCH'], 'column7': ['Apr-21', 'Apr-21', 'Apr-21', 'Apr-21', 'Apr-21', 'Apr-21', 'Apr-21', 'Apr-21', 'Mar-21', 'Mar-21', 'Mar-21', 'Mar-21', 'Mar-21', 'Mar-21', 'Apr-21', 'Apr-21', 'Mar-21', 'Mar-21'], 'Feb-21': [nan, nan, nan, nan, nan, nan, nan, nan, nan, nan, nan, nan, nan, nan, nan, nan, nan, nan], 'Mar-21': [nan, nan, nan, nan, nan, nan, nan, nan, nan, -16474.0, nan, nan, nan, -19946.0, nan, nan, nan, nan], 'Apr-21': [nan, nan, nan, nan, nan, nan, nan, nan, -17001.0, nan, nan, nan, nan, -11548.0, nan, -5297.073170731703, nan, -282.0], 'May-21': [nan, nan, nan, nan, nan, nan, nan, -18.0, nan, -12391.0, nan, nan, nan, -8686.0, nan, -35.8231707317027, -369.0, nan], 'Jun-21': [nan, nan, nan, nan, nan, nan, nan, nan, nan, nan, nan, -19.0, -4750.0, nan, -92.0, nan, -280.0, nan], 'Jul-21': [nan, -43949.0, nan, nan, nan, -79696.0, nan, -7291.0, nan, nan, nan, -19.0, -15746.0, nan, nan, -34.8231707317027, -319.0, nan], 'Aug-21': [nan, -8451.0, nan, -27973.0, nan, -675.0, -1423.0, nan, nan, nan, nan, -19.0, nan, -10480.0, nan, nan, -341.0, nan], 'Sep-21': [nan, nan, nan, -25934.0, nan, nan, nan, -5898.0, nan, nan, nan, -19.0, nan, nan, nan, nan, nan, -1591.0], 'Oct-21': [nan, nan, -4773.0, nan, nan, nan, -709.0, nan, nan, nan, nan, -19.0, nan, nan, nan, nan, nan, -1591.0]}

注意:我已经根据提供的测试数据实现了我的代码。示例数据仅关注应该被操作的列。

【问题讨论】:

  • 似乎发布 df2 的空间较小。我将在下面的 cmets 中发布它。
  • 您应该发布允许您到达第 3 步的代码。
  • @SergeBallesta,我已经用代码更新了问题。我尝试为测试数据实施到 Step4.2,但无法获得 100% 正确的预期输出。我希望代码有所帮助,详尽的解释有所帮助。如果您有任何问题,请告诉我。
  • 7 月 21 日的 df1 中有一个错字。图像中的第一个值是 30,而在数据集中,您提供的是 -30。
  • @AkshaySehgal - 感谢您发现它。修好了。

标签: python pandas dataframe


【解决方案1】:

这个呢?

对于每一步,我将column1 分组,然后将column4 设置为索引,并根据您的标准处理转置矩阵。请注意,我已经根据您的标准进行了一些推断,以匹配您参加的结果(我希望它是正确的,但您必须检查一下)。

还请注意,我已将每个步骤分开以使其更易于阅读。但是,一次完成分组/索引/转置并从那里处理您的算法会更有效。

import pandas as pd
import numpy as np
pd.set_option('display.max_columns', None)


df1 = pd.DataFrame(
        {'column1': ['ABC', 'ABC', 'CDF', 'CDF'], 'column4': ['A', 'B', 'A', 'B'], 'Feb-21': [0, 10, 0, 0], 'Mar-21': [0, 0, 70, 70], 'Apr-21': [-10, -10, -8, 60], 'May-21': [-30, -60, -10, 40], 'Jun-21': [-20, 9, -40, -20], 'Jul-21': [30, -10, 0, -20], 'Aug-21': [-30, -20, 0, -20], 'Sep-21': [0, -15, 0, -20], 'Oct-21': [0, -15, 0, -20]}
        )

print(df1)
print('-'*50)

df = df1.copy()

#step1 :
df.iloc[:, 2:] = df.iloc[:, 2:].where(df.iloc[:, 2:] < 0, np.nan)


print(df)
print('-'*50)

#step2 :
def step2(df):
    df = df.set_index("column4").T
    ix = df[(df.A<=0) & (df.B.isnull())].index
    df.loc[ix, "A"] = np.nan
    return df.T
df = df.groupby('column1').apply(step2)
df.reset_index(drop=False, inplace=True)
print(df)
print('-'*50)


#step3 :
def step3(df):
    df = df.set_index("column4").T
    ix = df[(df.A.isnull()) & (df.B>=0)].index
    df.loc[ix, "B"] = df.loc[ix, "A"]
    return df.T
df = df.groupby('column1').apply(step3)
df.reset_index(drop=False, inplace=True)
print(df)
print('-'*50)

#step4 :
def step4(df):
    df = df.set_index("column4").T
    a_pos = df.columns.get_loc('A')
    b_pos = df.columns.get_loc('B')
    #step 4.1
    ix = df[(df.A<0) & (df.B<0)].index
    if len(ix):
        ix = df.index.get_indexer(ix)
        left_pos = ix-1
        condition_left = df.iloc[left_pos].notnull().any(axis=1)
        ix = condition_left[condition_left].index
        ix = df.index.get_indexer(ix)
        df.iloc[ix+1, b_pos] = np.nan
    
    #step 4.2
    ix = df[(df.A<0) & (df.B<0)].index
    if len(ix):
        ix = df.index.get_indexer(ix)
        left_pos = ix-1
        condition_left = df.iloc[left_pos].isnull().all(axis=1)    
        ix = condition_left[condition_left].index
        ix = df.index.get_indexer(ix)
        df.iloc[ix+1, a_pos] = np.nan
    
    #step 4.1 (again)
    ix = df[(df.A<0) & (df.B<0)].index
    if len(ix):
        ix = df.index.get_indexer(ix)
        left_pos = ix-1
        condition_left = df.iloc[left_pos].notnull().any(axis=1)
        ix = condition_left[condition_left].index
        ix = df.index.get_indexer(ix)
        df.iloc[ix+1, b_pos] = np.nan
        
    return df.T

df = df.groupby('column1').apply(step4)
df.reset_index(drop=False, inplace=True)
print(df)
print('-'*50)

编辑

我在这里假设(根据您之前的评论)您的数据框将始终由交替的 A/B 行组成(并且数据框中的顺序有效)。然后我们需要计算一个人工索引来识别每一对行。

请注意,我在您的列(主要是 column5)上使用了 fillna,因为这是使用 groupby 命令的好习惯。由于多层次的列,我不确定它是否会产生影响......

在管理多级列时,布尔索引开始变得棘手。你会看到我会将每个“列”计算为一个 numpy.array(使用.values 方法)。不知何故,pandas 不会在多列上执行布尔匹配,我不确定为什么。

原来如此:

df = pd.DataFrame(  ...  )

#Compute the unique index for each pair of rows
df.reset_index(drop=False, inplace=True)
ix = df.index
ix = ix[ix%2==0]
df.loc[ix, 'index'] = df.shift(-1).loc[ix, 'index']

#step1 :
cols = [x for x in df.columns.tolist() if not x.startswith('column') and x != "index"]
df[cols] = df[cols].where(df[cols] < 0, np.nan)


cols_index = ["column4", "column1", "column2", "column3", "column5", "column6", "column7"]
df[cols_index] = df[cols_index].fillna(-1)

#step2 :
def step2(df):
    df = df.set_index(cols_index).drop('index', axis=1).T
    ix = df[
            (df.A<=0).values
            & df.B.isnull().values
         ].index
    df.loc[ix, "A"] = np.nan
    return df.T
df = df.groupby('index').apply(step2)
print(df)
df.reset_index(drop=False, inplace=True)
print(df)
print('-'*50)


#step3 :
def step3(df):
    df = df.set_index(cols_index).drop('index', axis=1).T
    ix = df[
            df.A.isnull().values 
            & (df.B>=0).values
            ].index
    df.loc[ix, "B"] = df.loc[ix, "A"]
    return df.T
df = df.groupby('index').apply(step3)
df.reset_index(drop=False, inplace=True)
print(df)
print('-'*50)

#step4 :
def step4(df):
    df = df.set_index(cols_index).drop('index', axis=1).T
    a_pos = df.columns.get_loc('A')
    b_pos = df.columns.get_loc('B')
    
    #step 4.1
    ix = df[
            (df.A<0).values
            & (df.B<0).values
            ].index
    if len(ix):
        ix = df.index.get_indexer(ix)
        left_pos = ix-1
        condition_left = df.iloc[left_pos].notnull().any(axis=1)
        ix = condition_left[condition_left].index
        ix = df.index.get_indexer(ix)
        df.iloc[ix+1, b_pos] = np.nan
    
    #step 4.2
    ix = df[
            (df.A<0).values
            & (df.B<0).values
            ].index
    if len(ix):
        ix = df.index.get_indexer(ix)
        left_pos = ix-1
        condition_left = df.iloc[left_pos].isnull().all(axis=1)    
        ix = condition_left[condition_left].index
        ix = df.index.get_indexer(ix)
        df.iloc[ix+1, a_pos] = np.nan
    
    #step 4.1 (again)
    ix = df[
            (df.A<0).values
            & (df.B<0).values
            ].index
    if len(ix):
        ix = df.index.get_indexer(ix)
        left_pos = ix-1
        condition_left = df.iloc[left_pos].notnull().any(axis=1)
        ix = condition_left[condition_left].index
        ix = df.index.get_indexer(ix)
        df.iloc[ix+1, b_pos] = np.nan
        
    return df.T

df = df.groupby('index').apply(step4)
df.reset_index(drop=False, inplace=True)
print(df)
print('-'*50)

如果你想恢复你的 column5 :

df[cols_index] = df[cols_index].replace(-1, np.nan)

【讨论】:

  • 谢谢,您是否尝试使用测试数据运行您的算法?如果是这样,我可以知道我需要对您当前的代码进行哪些更改吗?
  • 所以,我确实尝试使用测试数据运行您的代码,但在步骤 2 中出现以下错误,ValueError: cannot reindex from a duplicate axis
  • 您对如何处理这个问题有什么见解吗?或者如果有任何不清楚的地方,请告诉我,因为我觉得这真的很接近解决方案。
  • 已编辑我的答案。希望这将与您的真实数据集匹配(我的代码应该使我之前的评论更加具体)
  • 随时!如果那是因为我的解释不够清楚,请随时与我分享有问题的部分;-)
猜你喜欢
  • 2017-01-20
  • 2022-07-29
  • 1970-01-01
  • 1970-01-01
  • 2020-02-08
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-11-20
相关资源
最近更新 更多