【问题标题】:Load Json from Complicated Csv/DataFrame, retain dtypes for MongoDB从复杂的 Csv/DataFrame 加载 Json,为 MongoDB 保留 dtypes
【发布时间】:2018-11-13 21:41:00
【问题描述】:

我正在尝试从一些不同的 csv/excel 文件中构建一个可查询的 MongoDB 的 json 树。数据通常不完整,并通过主题 ID 链接。

以下示例数据:

subid,firstvisit,name,contact,dob,gender,visitdate1,age,visitcategory,samplenumber,label_on_sample,completed_by
    1,12/31/11,Bob,,12/31/00,Male,,,,,,
    1,,,,,,12/31/15,17,Baseline Visit,,,
    1,,,,,,12/31/16,18,Follow Up Visit,,,
    1,,,,,,12/31/17,18,Follow Up Visit,,,
    1,,,,12/31/00,Male,,17,,XXX123,1,Sally
    2,1/1/12,,,1/1/01,Female,,,,,,
    2,,,,,,1/1/11,10,Baseline Visit,,,
    2,,,,,,1/1/12,11,Follow Up Visit,,,
    2,,,,,,1/1/13,12,Follow Up Visit,,,
    2,,,,,,1/1/14,13,Follow Up Visit,,,
    2,,,,,,1/1/15,14,Follow Up Visit,,,
    2,,,,1/1/01,Female,,15,,YYY456,2,
    2,,,,1/1/01,Female,,15,,ZZZ789,2,Sally'

我希望输出看起来像这样:

[
    {
        "subject_id": "1",
        "name": "Bob",
        "dob": "12/31/00",
        "gender": "Male",
        "visits": {
            "12/31/15": {
                "age": "17",
                "visit_category": "Baseline Visit"
            },
            "12/31/16": {
                "age": "18",
                "visit_category": "Follow Up Visit"
            },
            "12/31/17": {
                "age": "18",
                "visit_category": "Follow Up Visit"
            }
        },
        "samples": {
            "XXX123": {
                "completed_by": "Sally",
                "label_on_sample": "1"
            }
        }
    },
    {
        "subject_id": "2",
        "name": null,
        "dob": "1/1/01",
        "gender": "Female",
        "visits": {
            "1/1/11": {
                "age": "10",
                "visit_category": "Baseline Visit"
            },
            "1/1/12": {
                "age": "11",
                "visit_category": "Follow Up Visit"
            },
            "1/1/13": {
                "age": "12",
                "visit_category": "Follow Up Visit"
            },
            "1/1/14": {
                "age": "13",
                "visit_category": "Follow Up Visit"
            },
            "1/1/15": {
                "age": "14",
                "visit_category": "Follow Up Visit"
            }
        },
        "samples": {
            "YYY456": {
                "completed_by": null,
                "label_on_sample": "2"
            },
            "ZZZ789": {
                "completed_by": "Sally",
                "label_on_sample": "2"
            }
        }
    }
]

我有一个程序可以将所有这些都放入正确的结构中,但不幸的是,因为它使用 csv 的 DictReader,似乎所有变量都作为字符串输入,因此很难以有意义的方式进行查询。这段代码如下:

def solution(csv_filename):
    by_subject_id = defaultdict(lambda: {
        'name': None,
        'dob': None,
        'gender': None,
        'visits': {},
        'samples': {}
    })

    with open(csv_filename) as f:
        dict_reader = DictReader(f)
        for row in dict_reader:
            non_empty = {k: v for k, v in row.items() if v}
            subject_id = non_empty['subid']  # must have to group by
            first_visit = non_empty.get('firstvisit')  # optional
            sample = non_empty.get('samplenumber')  # optional
            visit = non_empty.get('visitdate1')  # optional

            if first_visit:
                by_subject_id[subject_id].update({
                    'name': non_empty.get('name'),
                    'dob': non_empty.get('dob'),
                    'gender': non_empty.get('gender')
                })
            elif visit:
                by_subject_id[subject_id]['visits'][visit] = {
                    'age': non_empty.get('age'),
                    'visit_category': non_empty.get('visitcategory')
                }
            elif sample:
                by_subject_id[subject_id]['samples'][sample] = {
                    'completed_by': non_empty.get('completed_by'),
                    'label_on_sample': non_empty.get('label_on_sample')
                }
    return [{'subject_id': k, **v} for k, v in by_subject_id.items()]

解决此问题的最佳方法是什么?我可以将其转换为适用于数据框并希望保留数据类型吗?

非常感谢您的任何建议。 Mongo 新手,只是想获得一些有用的东西。

【问题讨论】:

  • 如果我理解问题,例如"subject_id": "1",您想要更好的"subject_id": 1,其中 1 是整数而不是字符串?如果是的话,你可能可以在你的代码中做'subject_id': int(k) 和其他值的相同想法
  • @Ben.T 这适用于“subject_id”,但不适用于其他字段,因为它在遇到许多 NaN 时会中断。谢谢您的帮助。部分解决了,但更大的问题仍然存在。
  • 您想在subject_id旁边更改哪个标签?我猜 agelabel_on_sampleint 但是你需要像 dob 这样的日期转换为 datetime 对象吗?
  • 理想情况下。在数据帧的清理和处理过程中,所有数据都被分配了一个类型。保留这些类型会很棒。

标签: python json mongodb pandas


【解决方案1】:

这里不是最好的解决方案,但是使用 pandas 可能有助于保持值的类型,我没有看代码的效率,只是读取 csv 文件的部分,但你可以这样做:

import pandas as pd
def solution(csv_filename):
    by_subject_id = defaultdict(lambda: {
        .
        .
    })

    df = pd.read_csv(csv_filename).fillna('')
    for row in df .iterrows():
        non_empty = {k: v for k, v in row[1].iteritems() if  v != ''}
        subject_id = non_empty['subid']  # must have to group by
        .
        .
        .

我尽量保持几行不变,其他一切都一样。最终,如果您可以直接将清理后的 DF 作为参数而不是读取 csv 文件,那就更好了。否则,您可以在 read_csv() 中添加dtype= 如:

df = pd.read_csv(csv_filename,dtype={'subid':int, 'age':int}).fillna('')

添加您想要的任何类型。

希望对你有帮助

【讨论】:

  • 这很好用!太感谢了。这里和那里仍然存在一些问题,日期列被返回一个字符串而不是日期对象,但这解决了很多问题。再次感谢!
  • @jester_in_yellow 我不知道 MongoDB,但如果你想要日期对象,你可以尝试添加 dtype {'dob':pd.datetime} (千万不要尝试,所以不确定它是否有效)或在进入你的循环之前, df['dob'] = pd.to_datetime(df['dob']) 但是如果 MongoDB 不识别 python datetime 对象,则没有必要
猜你喜欢
  • 1970-01-01
  • 2018-10-17
  • 2019-05-07
  • 2017-07-27
  • 1970-01-01
  • 1970-01-01
  • 2020-09-29
  • 2017-10-20
  • 1970-01-01
相关资源
最近更新 更多