【问题标题】:Conditional generation of new column - Pandas新列的条件生成 - Pandas
【发布时间】:2020-02-29 09:59:53
【问题描述】:

我正在尝试根据现有列的条件逻辑创建一个新列。我知道可能有更有效的方法来实现这一点,但我有一些条件需要包括在内。这只是第一步。

总体范围是创建两个从12 映射的新列。这些被引用到Object 列,因为每个时间点我可以有多行。

Object2Value 确定如何映射新列。因此,如果Value is == X,我想同时匹配Object 列以将该时间点对应的12 返回到一个新列。如果Value is == Y 应该发生相同的过程。如果Value is == Z,我想插入0, 0。其他的都应该是NaN

df = pd.DataFrame({   
        'Time' : ['2019-08-02 09:50:10.1','2019-08-02 09:50:10.1','2019-08-02 09:50:10.2','2019-08-02 09:50:10.3','2019-08-02 09:50:10.3','2019-08-02 09:50:10.4','2019-08-02 09:50:10.5','2019-08-02 09:50:10.6','2019-08-02 09:50:10.6'],
        'Object' : ['B','A','A','A','C','C','C','B','B'],
        '1' : [1,3,5,7,9,11,13,15,17],  
        '2' : [0,1,4,6,8,10,12,14,16],     
        'Object2' : ['A','A',np.nan,'C','C','C','C','B','A'],                 
        'Value' : ['X','X',np.nan,'Y','Y','Y','Y','Z',np.nan],                  
        })

def map_12(df):

for i in df['Value']:
    if i == 'X':
        df['A1'] = df['1']
        df['A2'] = df['2']
    elif i == 'Y':
        df['A1'] = df['1']
        df['A2'] = df['2']     
    elif i == 'Z':
        df['A1'] = 0
        df['A2'] = 0             
    else:
        df['A1'] = np.nan
        df['A2'] = np.nan              

return df

预期输出:

                    Time Object   1   2 Object2 Value    A1    A2
0  2019-08-02 09:50:10.1      A   1   0       A     X   1.0   0.0 # Match A-A at this time point, so output is 1,0
1  2019-08-02 09:50:10.1      B   3   1       A     X   1.0   0.0 # Still at same time point so use 1,0 
2  2019-08-02 09:50:10.2      A   5   4     NaN   NaN   NaN   NaN # No Value so NaN
3  2019-08-02 09:50:10.3      C   7   6       C     Y   7.0   6.0 # Match C-C at this time point, so output is 7,6
4  2019-08-02 09:50:10.3      A   9   8       C     Y   7.0   6.0 # Still at same time point so use 7,6 
5  2019-08-02 09:50:10.4      C  11  10       C     Y  11.0  10.0 # Match C-C at this time point, so output is 11,10
6  2019-08-02 09:50:10.5      C  13  12       C     Y  13.0  12.0 # Match C-C at this time point, so output is 13,12
7  2019-08-02 09:50:10.6      B  15  14       B     Z   0.0   0.0 # Z so 0,0
8  2019-08-02 09:50:10.6      B  17  16       A   NaN   NaN   NaN # No Value so NaN

新样本df:

 df = pd.DataFrame({   
        'Time' : ['2019-08-02 09:50:10.1','2019-08-02 09:50:10.1','2019-08-02 09:50:10.2','2019-08-02 09:50:10.3','2019-08-02 09:50:10.3','2019-08-02 09:50:10.4','2019-08-02 09:50:10.5','2019-08-02 09:50:10.6','2019-08-02 09:50:10.6'],
        'Object' : ['B','A','A','A','C','C','C','B','B'],
        '1' : [1,3,5,7,9,11,13,15,17],  
        '2' : [0,1,4,6,8,10,12,14,16],     
        'Object2' : ['A','A',np.nan,'C','C','C','C','B','A'],                 
        'Value' : ['X','X',np.nan,'Y','Y','Y','Y','Z',np.nan],                
        })

预期输出:

                    Time Object   1   2 Object2 Value    A1    A2
0  2019-08-02 09:50:10.1      B   1   0       A     X   3.0   1.0 # Match A-A at this time point, so output is 3,1
1  2019-08-02 09:50:10.1      A   3   1       A     X   3.0   1.0 # Still at same time point so use 3,1 
2  2019-08-02 09:50:10.2      A   5   4     NaN   NaN   NaN   NaN # No Value so NaN
3  2019-08-02 09:50:10.3      A   7   6       C     Y   9.0   8.0 # Match C-C at this time point, so output is 9,8
4  2019-08-02 09:50:10.3      C   9   8       C     Y   9.0   8.0 # Still at same time point so use 9,8 
5  2019-08-02 09:50:10.4      C  11  10       C     Y  11.0  10.0 # Match C-C at this time point, so output is 11,10
6  2019-08-02 09:50:10.5      C  13  12       C     Y  13.0  12.0 # Match C-C at this time point, so output is 13,12
7  2019-08-02 09:50:10.6      B  15  14       B     Z   0.0   0.0 # Z so 0,0
8  2019-08-02 09:50:10.6      B  17  16       A   NaN   NaN   NaN # No Value so NaN

【问题讨论】:

  • 您遇到了什么问题?而不是df['A1'] = df['1']df['A1'] = 1

标签: python pandas conditional-statements


【解决方案1】:

使用DataFrame.where + DataFrame.eq 创建一个类似于df[['1','2']] 的DataFrame 但仅适用于匹配为True 的行,其余为NaN。 然后使用DataFrame.groupby按时间点分组,并用ObjectObject2(matches==True)重合的现有值填入每组的缺失数据。使用DataFrame.where 舍弃df['Value']NaN 的值。当Z 在列Value 中时,最后使用[DataFrame.mask] 设置0

#matches
matches=df.Object.eq(df.Object2)
#Creating conditions
condition_z=df['Value']=='Z'
not_null=df['Value'].notnull()
#Creating DataFrame to fill
df12=( df[['1','2']].where(matches)
                    .groupby(df['Time'],sort=False)
                    .apply(lambda x: x.ffill().bfill()) )
#fill 0 on Value is Z and discarting NaN
df[['A1','A2']] =df12.where(not_null).mask(condition_z,0)
print(df)

输出

                    Time Object   1   2 Object2 Value    A1    A2
0  2019-08-02 09:50:10.1      B   1   0       A     X   3.0   1.0
1  2019-08-02 09:50:10.1      A   3   1       A     X   3.0   1.0
2  2019-08-02 09:50:10.2      A   5   4     NaN   NaN   NaN   NaN
3  2019-08-02 09:50:10.3      A   7   6       C     Y   9.0   8.0
4  2019-08-02 09:50:10.3      C   9   8       C     Y   9.0   8.0
5  2019-08-02 09:50:10.4      C  11  10       C     Y  11.0  10.0
6  2019-08-02 09:50:10.5      C  13  12       C     Y  13.0  12.0
7  2019-08-02 09:50:10.6      B  15  14       B     Z   0.0   0.0
8  2019-08-02 09:50:10.6      B  17  16       A   NaN   NaN   NaN

我们也可以使用GroupBy.transform

#matches
matches=df.Object.eq(df.Object2)
#Creating conditions
condition_z=df['Value']=='Z'
not_null=df['Value'].notnull()
#Creating DataFrame to fill
df12=( df[['1','2']].where(matches)
                    .groupby(df['Time'],sort=False)
                    .transform('first') )
#fill 0 on Value is Z and discarting NaN
df[['A1','A2']] =df12.where(not_null).mask(condition_z,0)
print(df)

【讨论】:

  • 感谢@ansev 的努力,但我的预期输出有点复杂。我添加了突出显示逻辑的 cmets。
  • 感谢您的说明。更新了我的解决方案的所有方法。请检查:)
  • 感谢 ansev,但我的预期输出有点不同。我需要为每个时间点匹配Object 列并填写到下一个时间点。那有意义吗。我的预期输出已针对每一行进行了注释。
  • 请注意,Object 和 Object2 之间的等价关系按时间分组,并使用 transform ('any') 进行扩展
  • 看看我的预期输出。索引 1 和索引 4 在同一时间点,所以我仍然想使用前几行中匹配的数字。
【解决方案2】:

如果只有少数条件使用DataFrame.loc按条件赋值:

m1 = df['Value'].isin(['X','Y'])
m2 = df['Value'] == 'Z'

df[['A1','A2']] = df.loc[m1, ['1','2']]
df.loc[m2, ['A1','A2']] = 0
print(df)
                    Time Object   1   2 Object2 Value   A1   A2
0  2019-08-02 09:50:10.1      A   1   0       A     X  1.0  0.0
1  2019-08-02 09:50:10.1      B   1   1       A     X  1.0  1.0
2  2019-08-02 09:50:10.2      A   5   4     NaN   NaN  NaN  NaN
3  2019-08-02 09:50:10.3      C   7   6       C     Y  7.0  6.0
4  2019-08-02 09:50:10.3      A   9   8       C     Y  9.0  8.0
5  2019-08-02 09:50:10.4      C  11  10     NaN   NaN  NaN  NaN
6  2019-08-02 09:50:10.5      C  13  12       B   NaN  NaN  NaN
7  2019-08-02 09:50:10.6      B  15  14       B     Z  0.0  0.0
8  2019-08-02 09:50:10.6      B  17  16       B   NaN  NaN  NaN

numpy.select 和广播掩码的另一种解决方案:

m1 = df['Value'].isin(['X','Y'])
m2 = df['Value'] == 'Z'

masks = [m1.values[:, None], m2.values[:, None]]
values = [df[['1','2']].values, 0]

df[['A1','A2']] = pd.DataFrame(np.select(masks,values, default=np.nan), index=df.index)
print(df)
                    Time Object   1   2 Object2 Value   A1   A2
0  2019-08-02 09:50:10.1      A   1   0       A     X  1.0  0.0
1  2019-08-02 09:50:10.1      B   1   1       A     X  1.0  1.0
2  2019-08-02 09:50:10.2      A   5   4     NaN   NaN  NaN  NaN
3  2019-08-02 09:50:10.3      C   7   6       C     Y  7.0  6.0
4  2019-08-02 09:50:10.3      A   9   8       C     Y  9.0  8.0
5  2019-08-02 09:50:10.4      C  11  10     NaN   NaN  NaN  NaN
6  2019-08-02 09:50:10.5      C  13  12       B   NaN  NaN  NaN
7  2019-08-02 09:50:10.6      B  15  14       B     Z  0.0  0.0
8  2019-08-02 09:50:10.6      B  17  16       B   NaN  NaN  NaN

【讨论】:

  • 谢谢@jezrael,这只是有点复杂。我在预期的输出中包含了 cmets,并添加了一个新的示例 df 来突出显示这一点。
【解决方案3】:

看看Dataframe apply

df['A1'] = df.apply(lambda row: row['1'] if row['Value'] == 'X' else np.nan, axis=1)

【讨论】:

    【解决方案4】:

    我不得不对您的数据框进行一些调整,因为它与您的问题中的预期结果不符。

    df = pd.DataFrame(
        {
            "Time": [
                "2019-08-02 09:50:10.1",
                "2019-08-02 09:50:10.1",
                "2019-08-02 09:50:10.2",
                "2019-08-02 09:50:10.3",
                "2019-08-02 09:50:10.3",
                "2019-08-02 09:50:10.4",
                "2019-08-02 09:50:10.5",
                "2019-08-02 09:50:10.6",
                "2019-08-02 09:50:10.6",
            ],
            "Object": ["A", "B", "A", "C", "A", "C", "C", "B", "B"],
            "1": [1, 1, 5, 7, 9, 11, 13, 15, 17],
            "2": [0, 1, 4, 6, 8, 10, 12, 14, 16],
            "Object2": ["A", "A", np.nan, "C", "C", "C", "C", "B", "A"],
            "Value": ["X", "X", np.nan, "Y", "Y", "Y", "Y", "Z", np.nan],
        }
    )
    

    这是一个矢量化解决方案,应该在大数据上表现良好。

    第一步是确保数据帧按时间排序。

    df = df.sort_values("Time")
    

    复制第 1 列和第 2 列

    df["A1"] = df["1"]
    df["A2"] = df["2"]
    

    将使用索引值获取每个时间组的第一行。

    df = df.reset_index()
    

    我对 list/isin 解决方案不太满意。想知道是否有人知道一种不那么老套的方法吗?

    li = df.groupby("Time").index.first().tolist()
    
    print(li)
    [0, 2, 3, 5, 6, 7]
    
    print(df)
       index                   Time Object   1   2 Object2 Value  A1  A2
    0      0  2019-08-02 09:50:10.1      A   1   0       A     X   1   0
    1      1  2019-08-02 09:50:10.1      B   1   1       A     X   1   1
    2      2  2019-08-02 09:50:10.2      A   5   4     NaN   NaN   5   4
    3      3  2019-08-02 09:50:10.3      C   7   6       C     Y   7   6
    4      4  2019-08-02 09:50:10.3      A   9   8       C     Y   9   8
    5      5  2019-08-02 09:50:10.4      C  11  10       C     Y  11  10
    6      6  2019-08-02 09:50:10.5      C  13  12       C     Y  13  12
    7      7  2019-08-02 09:50:10.6      B  15  14       B     Z  15  14
    8      8  2019-08-02 09:50:10.6      B  17  16       A   NaN  17  16
    

    过滤数据框以获取除列表中的行之外的所有行,然后将它们设置为 np.NaN

    df.loc[~df.index.isin(li), ["A1", "A2"]] = np.NaN
    

    向前填充第一行值。

    df[["A1", "A2"]] = df[["A1", "A2"]].ffill(axis=0)
    

    将 z 设置为 0,将 np.NaN 设置为 np.NaN

    df.loc[df["Value"] == "Z", ["A1", "A2"]] = 0
    df.loc[df["Value"].isnull(), ["A1", "A2"]] = np.NaN
    

    删除索引列

    df = df.drop("index", axis=1)
    
    print(df)
                        Time Object   1   2 Object2 Value    A1    A2
    0  2019-08-02 09:50:10.1      A   1   0       A     X   1.0   0.0
    1  2019-08-02 09:50:10.1      B   1   1       A     X   1.0   0.0
    2  2019-08-02 09:50:10.2      A   5   4     NaN   NaN   NaN   NaN
    3  2019-08-02 09:50:10.3      C   7   6       C     Y   7.0   6.0
    4  2019-08-02 09:50:10.3      A   9   8       C     Y   7.0   6.0
    5  2019-08-02 09:50:10.4      C  11  10       C     Y  11.0  10.0
    6  2019-08-02 09:50:10.5      C  13  12       C     Y  13.0  12.0
    7  2019-08-02 09:50:10.6      B  15  14       B     Z   0.0   0.0
    8  2019-08-02 09:50:10.6      B  17  16       A   NaN   NaN   NaN
    

    【讨论】:

    • 谢谢@run-out,这是最接近的。我认为我们需要包含对Object 列的引用。这仅在首先列出参考值时才有效。我将添加一个新的示例 df 以显示错误。
    • 一次有多个条目,我如何确定应该使用哪个“参考值”?
    猜你喜欢
    • 1970-01-01
    • 2019-08-17
    • 2021-09-15
    • 1970-01-01
    • 1970-01-01
    • 2018-08-01
    • 2018-06-20
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多