像 Prolog 一样统一数据框列以删除重复项

Posted

技术标签:

【中文标题】像 Prolog 一样统一数据框列以删除重复项【英文标题】:Unify dataframe columns like in Prolog to remove duplicates 【发布时间】:2021-06-21 04:22:24 【问题描述】:

我正在学习 numpypandas,所以如果我没有使用正确的术语或者我问了一些愚蠢的问题,请原谅我。 我花了很长时间在网上搜索我的问题的答案,但没有运气......

我有几个 .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.NANnp.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 条件?

excel表格如何统一删除一串数字前面相同数字

查询删除重复项

power BI-数据处理(跟power query几乎一样)

Prolog - 确定列表中重复的数量并将数量转换为指数

从 pandas 数据框列中的对象中删除逗号