Группировка Python DataFrame преобразует NaN в None, чтобы сгенерировать действительный JSON

Я получаю несколько JSON, которые мне нужно преобразовать и объединить, что я делаю с пандами, а затем мне также нужно сгенерировать JSON. Структура итогового JSON фиксирована. иногда некоторые поля в JSON отсутствуют (и это правильно), но мне приходится сохранять поля в объединенном объекте, что тоже работает нормально. Мне нужно преобразовать все значения NaN в None, чтобы впоследствии получить действительный json с нулевыми значениями, но после операции groupby он преобразует некоторые значения None обратно в NaN.

См. прикрепленный пример:

import pandas as pd
import json
dict1 =  {
     "items": [
    {
      "name": "Project1",
      "projectId": "1",
    },
    {
      "name": "Project2",
      "projectId": "2",
    },
    {
      "name": "Project3",    
      "projectId": "3",
    }
    ]
}

dict2 = {
     "items": [
    {
      "attr1": "ABC",
      "attr2": "DEF1",
      "attr3": "GHI1",
      "projectId": "1",
      "services":[
          {
              "sname": "Service1",
          },
          {
              "sname": "Service2",
          }
      ]
    },
    {
      "attr1": "ABC",
      "attr2": "DEF2",
      "attr3": "GHI2",
      "projectId": "2",
      "services":[
          {
              "sname": "Service1",
          },
          {
              "sname": "Service2",
          }
      ]
    }
  ]
}

dict_head = {
    "id":"some-guid",
    "name":"some name",
    "content" :[
    ]
}

df1 = pd.DataFrame(dict1["items"])
# df2 = pd.DataFrame(dict2["items"])
df2 = pd.json_normalize(
        data = dict2['items'],
        record_path = ['services'], 
        meta = [
            'projectId', 
            'attr1',
            'attr2',
            'attr3'
        ]
    )


df_joined = df1.set_index("projectId").join(df2.set_index("projectId"))
print("df_joined_1")
print(df_joined)

#convert all NaN vales to None whichs works well
df_joined= df_joined.where(pd.notnull(df_joined), None)
print("df_joined_2")
print(df_joined)

df_grouped = df_joined.groupby(['projectId','name','attr1','attr2','attr3'], dropna=False)['sname'].apply(list).reset_index().to_dict(orient='records')
#suddenly the None values of the grouped fields are conveted back to NaN???
print("df_grouped:")
print(df_grouped)

df_grouped = [{'type': 'Project', 'id': d['projectId'], 'data': d} for d in df_grouped.to_dict(orient='records')]

dict_head["content"] = df_grouped
print("dict_head:")
print(dict_head)


print("dict_head as json:")
print(json.dumps(dict_head, indent=3))

выходные данные проекта 3, где вы видите как NaN, так и ноль, я ожидаю, что все NaN будут нулевыми значениями.

 {
      "type": "Project",
      "id": "3",
      "data": {
         "projectId": "3",
         "name": "Project3",
         "attr1": NaN,
         "attr2": NaN,
         "attr3": NaN,
         "sname": [
            null
         ]
      }
   }

забыл упомянуть операцию df_grouped = [{'type': 'Project', 'id': d['projectId'], 'data': d} for d in df_grouped.to_dict(orient='records')] добавил в свой вопрос

Fobber 26.06.2024 18:03
Почему в Python есть оператор "pass"?
Почему в Python есть оператор "pass"?
Оператор pass в Python - это простая концепция, которую могут быстро освоить даже новички без опыта программирования.
Некоторые методы, о которых вы не знали, что они существуют в Python
Некоторые методы, о которых вы не знали, что они существуют в Python
Python - самый известный и самый простой в изучении язык в наши дни. Имея широкий спектр применения в области машинного обучения, Data Science,...
Основы Python Часть I
Основы Python Часть I
Вы когда-нибудь задумывались, почему в программах на Python вы видите приведенный ниже код?
LeetCode - 1579. Удаление максимального числа ребер для сохранения полной проходимости графа
LeetCode - 1579. Удаление максимального числа ребер для сохранения полной проходимости графа
Алиса и Боб имеют неориентированный граф из n узлов и трех типов ребер:
Оптимизация кода с помощью тернарного оператора Python
Оптимизация кода с помощью тернарного оператора Python
И последнее, что мы хотели бы показать вам, прежде чем двигаться дальше, это
Советы по эффективной веб-разработке с помощью Python
Советы по эффективной веб-разработке с помощью Python
Как веб-разработчик, Python может стать мощным инструментом для создания эффективных и масштабируемых веб-приложений.
0
1
53
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Вам не нужно явно конвертировать NaN в None или конвертировать фрейм данных в dict. Панды сделают это за вас, если вы воспользуетесь pandas.to_json.

Вы можете использовать это для content, а затем снова загрузить преобразованный json как list, чтобы поместить его в нужную структуру:

df_joined = df1.set_index("projectId").join(df2.set_index("projectId"))

json_str = (
    df_joined.groupby(["projectId", "name", "attr1", "attr2", "attr3"], dropna=False)[
        "sname"
    ]
    .apply(list)
    .reset_index()
    .to_json(orient = "records")
)

json_list = json.loads(json_str)

json_content = [{"type": "Project", "id": d["projectId"], "data": d} for d in json_list]

dict_head["content"] = json_content

json.dumps(dict_head, indent=3)
{
   "id": "some-guid",
   "name": "some name",
   "content": [
      {
         "type": "Project",
         "id": "1",
         "data": {
            "projectId": "1",
            "name": "Project1",
            "attr1": "ABC",
            "attr2": "DEF1",
            "attr3": "GHI1",
            "sname": [
               "Service1",
               "Service2"
            ]
         }
      },
      {
         "type": "Project",
         "id": "2",
         "data": {
            "projectId": "2",
            "name": "Project2",
            "attr1": "ABC",
            "attr2": "DEF2",
            "attr3": "GHI2",
            "sname": [
               "Service1",
               "Service2"
            ]
         }
      },
      {
         "type": "Project",
         "id": "3",
         "data": {
            "projectId": "3",
            "name": "Project3",
            "attr1": null,
            "attr2": null,
            "attr3": null,
            "sname": [
               null
            ]
         }
      }
   ]
}

ах, ладно, я забыл упомянуть одну вещь: я конвертирую в to_dict, потому что потом мне нужно сделать следующее: я, конечно, могу затем снова привести список обратно в DataFrame, чтобы использовать to_json, но, может быть, есть лучший способ?

Fobber 26.06.2024 17:59

@Fobber Для этой новой структуры вам понадобится больше шагов. Я обновил ответ соответственно.

e-motta 27.06.2024 00:54

спасибо @e-motta, все работает. но я изменил код вашего первого решения, поэтому мне нужно использовать только функцию pandas.to_json() при преобразовании dict в json, предварительно приведя его к DataFrame, потому что это немного быстрее, чем другое решение.

Fobber 28.06.2024 12:58

мне снова: D пришлось снова перейти на ваше решение, потому что другой создает json, окруженный [ ], не знаю, откуда он взялся. еще раз спасибо

Fobber 28.06.2024 15:20

Другие вопросы по теме