【问题标题】:Insert array from csv using pymongo使用 pymongo 从 csv 插入数组
【发布时间】:2018-10-26 01:23:18
【问题描述】:

我有一个 csv 文件,其中包含以下字符串格式的数组:

date,name,criteria
2018-05-16,John,"[{'age':35},{'birthyear':1983}]"
2018-05-16,Jane,"[{'age':36},{'birthyear':1982}]"

我正在使用 Python 和 pandas 和 numpy 来处理这个

我需要以下列格式将此文件导入 MongoDB 集合:

{
   "date":'2018-05-16',
   "name":"John",
   "criteria" : [
         {"age":35},
         {"birthyear" : 1983}
   ]
},
{
   "date":'2018-05-16',
   "name":"Jane",
   "criteria" : [
         {"age":36},
         {"birthyear" : 1982}
   ]
 }

`

我尝试使用 json formatter ,但插入 Mongodb 后,我得到的数组与 csv 文件中的相同。

我尝试了以下方法:

#Approach 1
import pymongo
from pymongo import MongoClient
import pandas as pd
import numpy as np
import json
from datetime import datetime

df = pd.read_csv("file.csv")
records = json.loads(df.T.to_json()).values()
db.tmp_collection.insert_many(data.to_dict('record'))



#Approach 2
import pymongo
from pymongo import MongoClient
import pandas as pd
import numpy as np
import json
from datetime import datetime

df = pd.read_csv("file.csv")
data_json = json.loads(df.to_json(orient='records'))
db.tmp_collection.insert_many(data_json)

两者都在 Mongodb 集合中给出以下输出:

{
"date" : "2018-05-16",
"name" : "John",
"criteria" : "[{age:35},{birthyear:1983}]"
}

你能推荐一些更好的方法吗? 附言我是 Python 新手。

提前致谢。

【问题讨论】:

  • 在 csv 中,“条件”值不是有效的 json,因为没有引用字符串(年龄、出生年份)。你能解决这个问题吗?

标签: python-3.x mongodb pandas numpy


【解决方案1】:

如前所述,主要问题是criteria 的“字符串”中的 JSON“like”数据在键周围缺少引号。使用正确的引号,您可以将字符串解析为列表,数据的结构可以按照您的意愿进行。

您实际上可以在现有列表上运行 mapre.sub() 并将 criteria 替换为解析后的版本。

鉴于您所说的形式的源数据:

date,name,criteria
2018-05-16,John,"[{age:35},{birthyear:1983}]"
2018-05-16,Jane,"[{age:36},{birthyear:1982}]"

那么重要的部分是:

df = pd.read_csv("file.csv")
records = json.loads(df.to_json(orient='records'))

pattern = r"({|,)(?:\s*)(?:')?([A-Za-z_$\.][A-Za-z0-9_ \-\.$]*)(?:')?(?:\s*):"

records = map(lambda x:
  dict(x.items() +
    {
      'criteria': json.loads(
        re.sub(pattern, "\\1\"\\2\":", x['criteria'])
      )
    }.items()
  ),
  records
)

这基本上是遍历列表中较早出现的每个项目,并对“字符串”进行替换以引用对象中的键。然后当然会将现在有效的 JSON 字符串解析为字典对象列表。

这将使数据如下:

[{u'criteria': [{u'age': 35}, {u'birthyear': 1983}],
  u'date': u'2018-05-16',
  u'name': u'John'},
 {u'criteria': [{u'age': 36}, {u'birthyear': 1982}],
  u'date': u'2018-05-16',
  u'name': u'Jane'}]

然后您可以将其传递给 insert_many() 以在集合中创建保持所需格式的文档。

db.tmp_collection.insert_many(records)

这里使用的正则表达式模式归因于regular expression to add double quotes around keys in javascript

我个人会更进一步,至少解析为datetime

records = map(lambda x:
  dict(x.items() +
    {
      'date': datetime.strptime(x['date'], '%Y-%m-%d'),
      'criteria': json.loads(
        re.sub(pattern, "\\1\"\\2\":", x['criteria'])
      )
    }.items()
  ),
  records
)

MongoDB 将在插入集合时使用 BSON 日期,这比字符串有用得多。

再说一次,“我个人”不会在 MongoDB 列表中使用“命名键”。相反,我宁愿“重新映射”到更标准的东西,例如 "k""v",如下所示:

records = map(lambda x:
  dict(x.items() +
    {
      'date': datetime.strptime(x['date'], '%Y-%m-%d'),
      'criteria':
      [i for s in map(lambda y: [{ 'k': k, 'v': v } for k,v, in y.iteritems()] , json.loads(
        re.sub(pattern, "\\1\"\\2\":", x['criteria'])
      )) for i in s]
    }.items()
  ),
  records
)

它给出了这样的结构:

[{u'criteria': [{'k': u'age', 'v': 35}, {'k': u'birthyear', 'v': 1983}],
  u'date': datetime.datetime(2018, 5, 16, 0, 0),
  u'name': u'John'},
 {u'criteria': [{'k': u'age', 'v': 36}, {'k': u'birthyear', 'v': 1982}],
  u'date': datetime.datetime(2018, 5, 16, 0, 0),
  u'name': u'Jane'}]

主要原因是如果路径更加一致,那么使用 MongoDB 进行“查询”将会更加有用。

【讨论】:

  • 获取错误JSONDecodeError: Expecting value: line 1 column 24 (char 23) 以插入集合。尝试使用 json.dumps 代替 json.loads 但这给出了TypeError: unsupported operand type(s) for +: 'dict_items' and 'dict_items' 错误
  • @cartman619 不是来自您在问题中包含的项目。对我来说很好,否则我不会在这里发布。我没有任何代码显示dict_items。执行与您实际要求的内容不同的内容,后果自负。
  • @cartman619 您是否更改了 CSV 数据源?你在问题中改变了它。如果您确实在 CSV 本身中添加了引号,那么您不需要在此处使用 re.sub 行,只需将 x['criteria'] 传递给 json.loads()。该替换的全部意义在于您的原始发布数据在键上没有这些引号,以使其成为有效的 JSON 解析。
  • 是的,我已更改为 csv 以反映正确性,对此感到抱歉。我评论了re.sub 部分,但在插入集合时仍然收到JSONDecodeError: Expecting value: line 1 column 24 (char 23) 错误。还有什么方法可以在json.loads() 之后打印records,这样我就可以检查为什么会出现这个错误
  • @cartman619 “插入集合时”是什么意思? records 只是一个字典列表,这就是你需要传递给.insert_many() 而无需做任何其他事情。这正是我向您展示的内容,但您似乎暗示您正在尝试在该内容上运行 loads()。您不要这样做,因为 pymongo 方法需要“python 数据”而不是 JSON。当然,您可以打印它。我在答案中显示pprint 输出,所以我就是这样做的。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2019-09-04
  • 2016-04-23
  • 2015-01-19
  • 1970-01-01
  • 2013-12-08
  • 1970-01-01
  • 2018-03-22
相关资源
最近更新 更多