我有一个旧的 Dataframe ,包含以下列和许多行,如下所示
>old_df
date/time Name detect_ID category ID
12/1/2023 XXX 1 B 1400
12/1/2023 XXY 1,3,7 B 1402
12/1/2023 XXY 4 A 1403
12/1/2023 XXY 4 B 1407
.....
我有一些关于new_df的信息,它有类似的列,基于此,我想更新old_df。新 Dataframe 为:
>new_df
date/time Name detect_ID category ID
13/1/2023 XXX 1 B 1400
14/1/2023 XXY 1,3,8 B 1402
14/1/2023 XXY 1 B 1405
.....
对于更新,我希望以下条件:
1.我想遍历old_df的行,同时检查new_df的每一行及其信息。但我只想检查 Category 列值为“B”的old_df的那些行。
1.首先,程序将记住第一行的 ID 值new_df用于第一次迭代(并使用连续迭代遍历new_df的所有行)。在遍历old_df的行时。如果new_df的第一行的 ID 与old_df的任何 ID 都不匹配,则将从new_df中获取整行并将其添加为old_df的新行,同时在old_df中创建一个名为 Identify 的新列并分配一个值 new。
如果此new_df的 ID 与old_df的任何 ID 匹配,则它将遍历该特定行的 detect_ID 列值。
答:如果old_df的特定 detect_ID 值与第一行的new_df的 detect_ID 值匹配,它将采用new_df的特定行并替换匹配的old_df行,而新创建的列 identify 的值将为 updated。在这种情况下。此外,如您所见,detect_ID 有多个值:我想分别检查它们,其中一些数字可能是整数,所以基本上用**,将它们拆分,并将它们转换为整数。
B.如果old_df的 detect_ID 值与第一行的new_df的 detect_ID 值不匹配,它将从new_df中取出整行,并将其添加为old_df的新行,同时进入列 identify 并分配值 new。
1.对于old_df的行,“ID”值与具有相同“ID”或相同“ID”但“detect_ID”值不匹配的new_df的任何行不匹配,将在old_df中保持不变,并在 identify 列中具有值 unchanged。
我希望它迭代old_df的所有行,直到new_df的每一行都在old_df**中更新。
对于给定的示例,我希望输出 Dataframe 如下所示:
>output
date/time Name detect_ID category ID identify
13/1/2023 XXX 1 B 1400 updated [Case A]
14/1/2023 XXY 1 B 1402 updated [Case A with multiple detect_ID]
14/1/2023 XXY 3 B 1402 updated
12/1/2023 XXY 7 B 1402 unchanged [Step 3, Id matches but detect_id do not ]
14/1/2023 XXY 8 B 1402 new [Case B]
12/1/2023 XXY 4 A 1403 unchanged
12/1/2023 XXY 4 B 1407 unchanged [Step3 , id not found in new_df]
我正在使用下面的代码,但它似乎没有工作的方式,我想要的。它给了很多重复,并没有迭代通过很多行的旧_df太多。
old_df = pd.read_csv('old.csv')
new_df = pd.read_csv('new.csv')
# Create a set of tuples representing the unique (ID, Detector Id) pairs in the old dataframe
unique_pairs = set()
for _, row in old_df.iterrows():
detector_ids = [int(x) for x in str(row['Detect_ID']).split(',')]
for detector_id in detector_ids:
unique_pairs.add((row['ID'], detect_id))
# Iterate over the rows in the new dataframe and check if their (ID, Detector Id) pair is in the set of unique pairs
new_rows = []
updated_rows = []
for _, row in new_df.iterrows():
detector_ids = [int(x) for x in str(row['Detect_ID']).split(',')]
for detector_id in detector_ids:
if (row['ID'], detector_id) in unique_pairs:
old_row = old_df.loc[(old_df['ID'] == row['ID']) & (old_df['Detect_ID'].str.contains(str(detector_id)))]
if not old_row.empty:
old_row = old_row.iloc[0]
old_row['Date/Time'] = row['date/time']
old_df.loc[(old_df['ID'] == row['ID']) & (old_df['Detector_ID'].str.contains(str(detector_id))), 'date/time'] = old_row['date/time']
updated_rows.append(old_row)
else:
row['Identify'] = 'new'
new_rows.append(row)
unique_pairs.add((row['ID'], detector_id))
# Append the new rows to the old dataframe and write the updated dataframe to a new file
old_df = old_df.append(new_rows, ignore_index=True)
for row in updated_rows:
row['Identify'] = 'updated'
old_df = old_df.append(updated_rows, ignore_index=True)
old_df.to_csv('updated.csv', index=False)
1条答案
按热度按时间zengzsys1#
依赖于
df.iterrows
几乎总是意味着在pandas
中的操作是次优的(例如,参见SO post)。使用
Series.str.split
和df.explode
将detect_ID
列中类似1,3,7
的条目放入单独的行中。同时应用于两个dfs
。让我们使用Series.astype
将detect_ID
中所有值的类型更改为int
(假设数据确实由数字字符组成)。因为我们只想检查
category
列中包含B
值的行,所以使用Series.eq
从new_df
中过滤掉任何非B
值(尽管在当前示例中不存在这样的值)。应用
df.merge
。我们希望在['Name','detect_ID','category', 'ID']
上合并,保留两边的所有条目(因此:how='outer'
),还添加了一个indicator
列(称为identify
),它将告诉我们每行的源代码。添加自定义后缀(例如,'_old'
代替默认的'_x'
)是为了清楚起见。在此阶段,我们要决定需要为列
date/time
保留哪个值。我们需要_new
中所有行的值(1)存在于dfs
中,并且由于left_only
条目在列date/time_new
中将具有NaN
值,因此我们可以依赖于Series.where
来实现这一点:以下工作尚待完成:
identify
的值。我们可以使用Series.map
来完成此操作。res
中选择正确的列。让我们使用df_old
中的列名加上df.loc
中的identify
,并为此链接df.sort_values
。这里也使用df.reset_index
。注意:正如@Ashyam在上面的注解中提到的,您想要的结果没有ID为
1405
的行,它只存在于df_new
中。我在这里假设您 * 确实 * 希望在新的df
中包含此条目。如果不希望,您可以如下所示将其删除:当然,该操作实际上可以已经应用于
new_df
,参见上面的列category
中的值B
的滤波器。