像 Prolog 一样统一数据框列以删除重复项
Posted
技术标签:
【中文标题】像 Prolog 一样统一数据框列以删除重复项【英文标题】:Unify dataframe columns like in Prolog to remove duplicates 【发布时间】:2021-06-21 04:22:24 【问题描述】:我正在学习 numpy
和 pandas
,所以如果我没有使用正确的术语或者我问了一些愚蠢的问题,请原谅我。
我花了很长时间在网上搜索我的问题的答案,但没有运气......
我有几个 .CSV 文件,其中包含同一域中的数据。
每个文件都有一个列,该列用作每一行的主键。
每个文件中主键列的名称可能不同。
每个文件可能具有或缺少其他文件的主键。
假设每个文件都包含大部分主键是安全的,因此必须引入一些 np.NAN
。
我正在尝试将所有数据加载并组合到一个数据集中,可能会删除“重复”列。 这是我做第一部分的方式(如果有更好的方法,请告诉我):
import pandas as pd
FILENAME_KEY_TABLE =
'file_001.csv': 'id',
'file_002.csv': 'ident',
'file_003.csv': 'cid',
'file_004.csv': 'id',
dataset, previous = pd.DataFrame(), None
for filename, key in listing.items():
resource = pd.read_csv(filename)
dataset = pd.merge(dataset, resource, left_on=[previous], right_on=[key], how='outer')
previous = key
生成的数据集肯定有重复的列。 例如,上面的代码保留了所有主键的副本。 此外,某些列在其他文件中有重复(名称相同或不同)。
我正在使用以下代码来识别数据集中的重复列(如果有更好的方法,请告诉我)。输出是一个字典,其键是重复列的名称,值是它们重复的原始列的名称集。
def get_duplicates(df: pd.DataFrame) -> Dict[str, Set[str]]:
result =
for x in range(df.shape[1]):
col = df.iloc[:, x]
original = df.columns.values[x]
for y in range(x + 1, df.shape[1]):
other = df.iloc[:, y]
if col.equals(other):
duplicate = df.columns.values[y]
result.setdefault(duplicate, set()).add(original)
return result
不幸的是,上面的代码将列作为一个整体进行比较,它没有将合并引入的任何np.NAN
视为潜在匹配。我天真地尝试替换:
if col.equals(other):
...
与:
if col.equals(other) or col.isnull() or other.isnull():
...
显然没有成功。我应该如何更改上面的行以按预期工作?
比如下面例子中的'A'
和'B'
列实际上是统一的:
df = pd.DataFrame(
'A': ['x', 'x', np.NAN, np.NAN],
'B': ['x', np.NAN, 'x', np.NAN],
, index=[0, 1, 2, 3])
统一的列是:
ps.DataGrame(
'Unified': ['x', 'x', 'x', np.NAN],
, index=[0, 1, 2, 3])
最后,我想在 Prolog 中实现类似统一的东西。
如果每个对应的值匹配或其中任何一个是 np.NAN
(np.NAN
像变量一样工作),则两列统一。
如果两列统一,我想保留具有更具描述性的标题(较长的标题)的列,将其内容与另一列中的值合并(将任何 np.NAN
替换为其他列中的相应值),并最终删除另一列。是否已经有一个函数或包可以做到这一点?
最后但并非最不重要的一点是,我注意到我的数据中的某些列是重复的,但由于它们包含具有不同精度级别的浮点数(即:3 位或 6 位十进制数字),因此它们没有被识别出来。有没有办法解决这个或类似的情况?同样,是否有任何可能有帮助的包或功能?
提前致谢!
【问题讨论】:
【参考方案1】:我修改了我的代码如下,但是它很慢,并且没有找到与以前的纯 python 实现相同的重复项。
import pandas as pd
import swifter
def get_duplicates(df: pd.DataFrame) -> List[Tuple[str, str]]:
result = []
for i, original in enumerate(df.columns, start=1):
for duplicate in df.columns[i:]:
if df.swifter.apply(lambda x: unifying(x[original], x[duplicate]), axis=1).all():
result.append((original, duplicate))
return result
def consolidate(df: pd.DataFrame, original: str, duplicate: str) -> None:
if df.swifter.apply(lambda x: unifying(x[original], x[duplicate]), axis=1).all():
if len(duplicate) > len(original):
original, duplicate = duplicate, original
df[original] = df.swifter.apply(lambda x: unification(x[original], x[duplicate]), axis=1)
df.drop(duplicate, axis=1, inplace=True)
def unifying(src: Any, tgt: Any) -> bool:
return src == tgt or src is np.NAN or tgt is np.NAN
def unification(src: Any, tgt: Any) -> Any:
return src if src == tgt or tgt is np.NAN else tgt
df = pd.DataFrame(
'A': ['x', 'x', np.NAN, np.NAN],
'B': ['x', np.NAN, 'x', np.NAN],
, index=[0, 1, 2, 3])
for original, duplicate in get_duplicates(df):
consolidate(df, original, duplicate)
也许是因为 unif* 方法中的比较考虑了数据的类型? (即:'1.0'!= 1.0)
【讨论】:
【参考方案2】:经过进一步研究,我相信我所追求的是(矩阵)相关性。
def find_correlations(df: pd.DataFrame, threshold: float = 0.95):
corr = df.corr().abs()
upper = corr.where(np.triu(np.ones(corr.shape), k=1).astype(bool))
result = []
for column in upper.columns:
key, value = max(upper[column].items(), key=lambda x: x[1])
if value > threshold:
result.append((column, key, value))
return result
【讨论】:
以上是关于像 Prolog 一样统一数据框列以删除重复项的主要内容,如果未能解决你的问题,请参考以下文章
如何检查同一数据框列中的重复值并通过根据频率删除行来应用 if 条件?