我们从外部实验室和中心收到了许多不同的CSV文件.当收到这样的文件时,我们首先需要做一些QA判断,然后才能进一步处理.因此,确保数据是正确的,至少在技术层面上是正确的.

我们有一些Python脚本来判断列数、判断日期值、最小/最大范围等.但现在我们还想判断枚举列是否正确.因此,例如,如果列访问是一个编码值,并且可能只包含baselinefup_6_mfup_12_m,则它不应该包含其他任何内容,如fup_36_m.

我们有元数据规范,因此列名和编码值列表(也称为枚举)是预先知道的.

这是我到目前为止得到的Python脚本:

# check if coded values are correct
import pandas as pd
import io

## load data from csv files
##df = pd.read_csv (r'patlist_mcl2017.csv', sep = ",", decimal=".")

# TESTING: create data frame from text
str_patients = """patid,dob,sex,height,score,visit
1072,16-01-1981,M,154,1,fup_12_m
1091,20-12-1991,M,168,4,baseline
1126,25-12-1999,M,181,3,fup_6_m
1139,14-04-1980,Y,165,1,baseline
1171,05-11-1984,M,192,2,fup_12_m
1237,17-08-1983,F,170,3,fup_6_m
1334,26-08-1985,F,160,5,fup_6_m
1365,14-09-1976,M,184,3,fup_24_m
1384,28-12-1993,F,152,1,baseline
1456,27-09-1998,F,164,5,fup_12_m
"""
df = pd.read_csv(io.StringIO(str_patients), sep = ",", decimal=".")

print(df)

# allowed values for enumeration columnms
allowed_enum = {
    'sex': ['M', 'F'],
    'score': [0, 1, 2, 3, 4],
    'visit': ['baseline', 'fup_6_m', 'fup_12_m']
}

# check enumeration
for column_name, allowed_values in allowed_enum.items():
    df_chk = df[~df[column_name].isin(allowed_values)].groupby(column_name).size().reset_index(name='Count')
    if not df_chk.empty:
        print("Found invalid values for column '%s':" % column_name)
        print(df_chk)

它工作正常,输出如下所示:

Found invalid values for column 'sex':
  sex  Count
0   Y      1
Found invalid values for column 'score':
   score  Count
0      5      2
Found invalid values for column 'visit':
      visit  Count
0  fup_24_m      1

但不同的文件可以包含许多列,为了更好地报告,我们希望将输出作为一个数据帧,因此如下所示:

  Column_name  Invalid  Count
0 Sex            Y          1
1 Score          5          2
2 visit       fup_24_m      1

所以我的问题是:

  • 如上所述,收集数据帧中的无效值的最佳方法是什么?
  • 或者,有没有更好的方法来判断/验证这些编码值?

推荐答案

你可以试一试

...
dfs = {
    column_name: df[~df[column_name].isin(allowed_values)]
                 .value_counts(subset=column_name)
                 .to_frame().reset_index(names="Invalid")
    for column_name, allowed_values in allowed_enum.items()
}
out = pd.concat(dfs, names=("Column_name", None)).droplevel(1)

得到

              Invalid  count
Column_name                 
sex                 Y      1
score               5      2
visit        fup_24_m      1

对于样例数据帧(另外.reset_index个将给出问题中的格式).

或者,类似于扎克·杨的建议,你可以这样做

...
columns = (
    df.loc[~df[column_name].isin(allowed_values), column_name]
    for column_name, allowed_values in allowed_enum.items()
)
out = pd.concat(columns, axis=1, sort=True)

得到 a sub-dataframe which contains only the invalid values

   sex  score     visit
3    Y    NaN       NaN
6  NaN    5.0       NaN
7  NaN    NaN  fup_24_m
9  NaN    5.0       NaN

Python相关问答推荐

如何让 turtle 通过点击和拖动来绘制?

大Pandas 胚胎中产生组合

使用新的类型语法正确注释ParamSecdecorator (3.12)

如果值发生变化,则列上的极性累积和

利用Selenium和Beautiful Soup实现Web抓取JavaScript表

如何根据一列的值有条件地 Select 前N组?

将标签移动到matplotlib饼图中楔形块的开始处

干燥化与列姆化的比较

关于两个表达式的区别

Polars map_使用多处理对UDF进行批处理

如何将返回引用的函数与pybind11绑定?

如何使用大量常量优化代码?

Polars时间戳同步延迟计算

替换包含Python DataFrame中的值的<;

在一个数据帧中,我如何才能发现每个行号是否出现在一列列表中?

与同步和异步客户端兼容的Python函数

高效地计算数字数组中三行上三个点之间的Angular

Pandas 删除只有一种类型的值的行,重复或不重复

Match-Case构造中的对象可调用性测试

Python:在cmd中添加参数时的语法