Python 通过基于具有不同索引的唯一值将值从第一个数据帧更新到第二个数据帧来迭代每一行,否则追加并分配新ID

Python 通过基于具有不同索引的唯一值将值从第一个数据帧更新到第二个数据帧来迭代每一行,否则追加并分配新ID,python,pandas,function,numpy,dataframe,Python,Pandas,Function,Numpy,Dataframe,如果匹配唯一值,则尝试将每一行从df1更新为df2。如果不是,则将该行附加到df2并分配新的ID列 df1(无ID列): df2: 更新的df2的期望输出: unique_value Status Price ID 0 xyz123 bad 6.67 1000 <-updated 1 xyz123 bad

如果匹配唯一值,则尝试将每一行从df1更新为df2。如果不是,则将该行附加到df2并分配新的ID列

df1(无ID列):

df2:

更新的df2的期望输出:

        unique_value        Status        Price        ID
0       xyz123              bad           6.67         1000     <-updated
1       xyz123              bad           6.67         1000     <-updated
2       xyz123              bad           6.67         1000     <-updated
3       xyz123              bad           6.67         1000     <-updated
4       xyz985              bad           1.31         1001
5       abc987              okay          4.56         1002
6       eff987              bad           1.75         1003     <-updated
7       asd541              excellent     8.85         1004
8       efg125              okay          5.77         1005     <-appended  
注意,我最初使用
pd.factorize
根据
df2
的唯一_值分配ID,值从
1000
1001
(依此类推)使用以下代码:
df2['ID']=pd.factorize(df2['unique_值'])[0]+1000

我尝试使用这个解决方案(),但是它索引了我的unique_value列,这阻止了我继续迭代另一个数据集

我们可以用什么方法来编写脚本


谢谢

我实施这两部分的策略解释如下

  • 更新现有行:
    df2
    可以通过更新,前提是
    df1
    中的行的形状正确地重新塑造为
    (1,3)
    pandas
    中的广播概念与
    numpy
    中的广播概念相同
  • 追加新行:假设连续索引从
    0
    开始计数,可以通过直接调用
    df2.loc[len(df2),:]=…
    轻松追加新行,其中
    len(df2)
    是索引列的下一个未使用的自然数。示例:
  • 此外,在我的解决方案中还构造了两个额外的状态变量,因为我认为它们比每次搜索整个
    df2
    更有效。如果这不是一个问题,它们当然可以被丢弃

    代码

    # additional state variables
    # 1. for the ID to be added
    current_max_id = df2["ID"].max()
    # 2. for matching unique_values, avoiding searching df2["unique_value"] every time
    current_value_set = set(df2["unique_value"].values)
    
    # match unique_value's using the state variable instead of `df2`
    mask = df1["unique_value"].isin(current_value_set)
    
    for i in range(len(df1)):
        
        # current unique_value from df1
        uv1 = df1["unique_value"][i]
        
        # 1. update existing
        if mask[i]:
            
            # broadcast df1 into the matched rows in df2 (mind the shape)
            df2.loc[df2["unique_value"] == uv1, ["unique_value", "Status", "Price"]] = df1.iloc[i, :].values.reshape((1, 3))
            
        # 2. append new
        else:
            # update state variables
            current_max_id += 1
            current_value_set.add(uv1)
            # append the row (assumes df2.index=[0,1,2,3,...])
            df2.loc[len(df2), :] = [df1.iloc[i, 0], df1.iloc[i, 1], df1.iloc[i, 2], current_max_id]
    
    df2
    Out[45]: 
      unique_value     Status  Price      ID
    0       xyz123        bad   6.67  1000.0
    1       xyz123        bad   6.67  1000.0
    2       xyz123        bad   6.67  1000.0
    3       xyz123        bad   6.67  1000.0
    4       xyz985        bad   1.31  1001.0
    5       abc987       okay   4.56  1002.0
    6       eff987        bad   1.75  1003.0
    7       asd541  excellent   8.85  1004.0
    8       efg125       okay   5.77  1005.0
    
    输出

    # additional state variables
    # 1. for the ID to be added
    current_max_id = df2["ID"].max()
    # 2. for matching unique_values, avoiding searching df2["unique_value"] every time
    current_value_set = set(df2["unique_value"].values)
    
    # match unique_value's using the state variable instead of `df2`
    mask = df1["unique_value"].isin(current_value_set)
    
    for i in range(len(df1)):
        
        # current unique_value from df1
        uv1 = df1["unique_value"][i]
        
        # 1. update existing
        if mask[i]:
            
            # broadcast df1 into the matched rows in df2 (mind the shape)
            df2.loc[df2["unique_value"] == uv1, ["unique_value", "Status", "Price"]] = df1.iloc[i, :].values.reshape((1, 3))
            
        # 2. append new
        else:
            # update state variables
            current_max_id += 1
            current_value_set.add(uv1)
            # append the row (assumes df2.index=[0,1,2,3,...])
            df2.loc[len(df2), :] = [df1.iloc[i, 0], df1.iloc[i, 1], df1.iloc[i, 2], current_max_id]
    
    df2
    Out[45]: 
      unique_value     Status  Price      ID
    0       xyz123        bad   6.67  1000.0
    1       xyz123        bad   6.67  1000.0
    2       xyz123        bad   6.67  1000.0
    3       xyz123        bad   6.67  1000.0
    4       xyz985        bad   1.31  1001.0
    5       abc987       okay   4.56  1002.0
    6       eff987        bad   1.75  1003.0
    7       asd541  excellent   8.85  1004.0
    8       efg125       okay   5.77  1005.0
    

    使用python 3.7、pandas 1.1.2、OS=debian 10 64位进行测试

    谢谢@Bill!快速提问,为什么在将df1广播到df2中的匹配行时需要重塑?我猜:如果主数组碰巧具有形状(3,3),并且您提供了形状(3,)的数组。那么你要广播哪个维度呢?作为(3,1)或(1,3)?因此,每当有这样的陷阱时,我自己总是显式地分配单例维度。再次感谢@Bill。知道为什么ID列返回为
    浮点值吗?那只是因为我在复制粘贴时忘记了重新格式化输入ID
    df2[“ID”]=df2[“ID”]。开始时的astype(int)
    解决了这个问题,但您在实际的工作流中不需要它,因为它是由在SO上重新导入文本数据引起的。非常感谢您的帮助@Bill!
    df2
    Out[45]: 
      unique_value     Status  Price      ID
    0       xyz123        bad   6.67  1000.0
    1       xyz123        bad   6.67  1000.0
    2       xyz123        bad   6.67  1000.0
    3       xyz123        bad   6.67  1000.0
    4       xyz985        bad   1.31  1001.0
    5       abc987       okay   4.56  1002.0
    6       eff987        bad   1.75  1003.0
    7       asd541  excellent   8.85  1004.0
    8       efg125       okay   5.77  1005.0