如何对比Google Cloud Bucket中多份CSV的DataFrame并提取非共同值?
问题描述
我在Google Cloud Bucket中存储了若干CSV文件,目标是对比每份文件中的ColumnA和ColumnB两列,输出那些并非在Bucket内所有CSV文件中都存在的值。目前我已能列出Bucket内的文件,通过循环将每份CSV读取为包含目标列的DataFrame并存储到字典中,但不清楚如何实现多DataFrame的对比提取。
现有代码如下:
import pandas as pd, numpy as np import os from google.cloud import storage bucketName = 'test_bucket_01' bucketFolder = 'test_folder_01' os.environ['GOOGLE_APPLICATION_CREDENTIALS'] = 'test.credentials.json' storage_client = storage.Client.from_service_account_json('test.credentials.json') bucket = storage_client.get_bucket(bucketName) file_location = f'gs://{bucketName}/{bucketFolder}' storage_blob_list = list(storage_client.list_blobs(bucketName, prefix=bucketFolder)) file_list = [blob_name.name[len(bucketFolder):] for blob_name in storage_blob_list if blob_name.name != bucketFolder and 'String' in blob_name.name] file_list = [element.replace("/","") for element in file_list] for fileName in file_list: d = {} data = pd.read_csv(f'{file_location}/{fileName}', usecols=['ColumnA','ColumnB']) d[fileName1] = data print(d[fileName])
4份DataFrame的输出如下:
ColumnA ColumnB 0 AA-1234 AA-1234-ABC 1 AA-1235 AA-1235-ABC 2 AA-1236 AA-1236-ABC 3 AA-1237 AA-1237-ABC ColumnA ColumnB 0 AA-1234 AA-1234-ABC 1 AA-1235 AA-1235-ABC 2 AA-1236 AA-1236-ABC 3 BB-1237 BB-1237-ABC ColumnA ColumnB 0 AA-1234 AA-1234-ABC 1 AA-1235 AA-1235-ABC 2 AA-1236 AA-1236-ABC 3 CC-1237 CC-1237-ABC ColumnA ColumnB 0 AA-1234 AA-1234-ABC 1 AA-1235 AA-1235-ABC 2 AA-1236 AA-1236-ABC 3 DD-1237 DD-1237-ABC
请问最简单的实现提取并输出非所有DataFrame共有的值的方法是什么?
解决方案
先修正代码里的小问题
你当前的循环每次都会重新初始化字典d,导致最后只保留最后一个文件的数据,而且还有fileName1的笔误。先把字典初始化移到循环外面:
# 把字典初始化放在循环外 df_dict = {} for fileName in file_list: df = pd.read_csv(f'{file_location}/{fileName}', usecols=['ColumnA','ColumnB']) df_dict[fileName] = df # 修正笔误,用fileName作为键
方法一:统计出现次数(推荐,能看来源文件)
核心逻辑是:把所有DataFrame的行合并,统计每组(ColumnA, ColumnB)在多少个文件里出现,筛选出出现次数不等于文件总数的行。
完整代码:
import pandas as pd import os from google.cloud import storage bucketName = 'test_bucket_01' bucketFolder = 'test_folder_01' os.environ['GOOGLE_APPLICATION_CREDENTIALS'] = 'test.credentials.json' storage_client = storage.Client.from_service_account_json('test.credentials.json') bucket = storage_client.get_bucket(bucketName) file_location = f'gs://{bucketName}/{bucketFolder}' storage_blob_list = list(storage_client.list_blobs(bucketName, prefix=bucketFolder)) file_list = [blob_name.name[len(bucketFolder):] for blob_name in storage_blob_list if blob_name.name != bucketFolder and 'String' in blob_name.name] file_list = [element.replace("/","") for element in file_list] # 存储所有DataFrame到字典 df_dict = {} for fileName in file_list: df = pd.read_csv(f'{file_location}/{fileName}', usecols=['ColumnA','ColumnB']) df_dict[fileName] = df # 合并所有DataFrame,标记来源文件 all_dfs = [] for name, df in df_dict.items(): df['source_file'] = name all_dfs.append(df) combined_df = pd.concat(all_dfs, ignore_index=True) # 统计每组值在多少个文件中出现 count_df = combined_df.groupby(['ColumnA', 'ColumnB'])['source_file'].nunique().reset_index(name='file_count') # 筛选出不是所有文件都有的行 total_files = len(file_list) non_common_rows = count_df[count_df['file_count'] != total_files] # 关联回原数据,查看这些值来自哪些文件 result = pd.merge(combined_df, non_common_rows, on=['ColumnA', 'ColumnB']) print(result)
方法二:取交集反向筛选(更简洁,不需要来源文件)
如果只需要知道哪些值不是公共的,不需要追踪来源,可以用交集的方式:
# 接前面的df_dict初始化代码 # 先取第一个文件的行作为初始公共基准 common_rows = df_dict[next(iter(df_dict.keys()))].copy() # 依次和其他文件取交集,最终得到所有文件共有的行 for df in df_dict.values(): common_rows = pd.merge(common_rows, df, on=['ColumnA', 'ColumnB'], how='inner') # 从所有行中排除公共行,得到非公共行 all_unique_rows = pd.concat(df_dict.values(), ignore_index=True).drop_duplicates() non_common_rows = all_unique_rows.merge(common_rows, on=['ColumnA', 'ColumnB'], how='left', indicator=True) non_common_rows = non_common_rows[non_common_rows['_merge'] == 'left_only'].drop('_merge', axis=1) print(non_common_rows)
输出结果
针对你的示例数据,两种方法都会输出以下非公共行:
ColumnA ColumnB 0 AA-1237 AA-1237-ABC 1 BB-1237 BB-1237-ABC 2 CC-1237 CC-1237-ABC 3 DD-1237 DD-1237-ABC
内容的提问来源于stack exchange,提问作者Pete
相关产品推荐
相关产品推荐

