Формат данных CSV во вложенном словаре

У меня есть формат csv ниже. Я хочу, чтобы он конвертировал какой-то вложенный словарь.

name,columns,tests
ABC_ESTIMATE_REFINED,cntquota,dbt_expectations.expect_column_to_exist
ABC_ESTIMATE_REFINED,cntquota,not_null
ABC_ESTIMATE_REFINED,is_purged,dbt_expectations.expect_column_to_exist
ABC_ESTIMATE_REFINED,is_purged,not_null

Ожидаемый результат

{
    "name": "ABC_ESTIMATE_REFINED",
    "columns": [
        {
            "name": "cntquota",
            "tests": [
                "dbt_expectations.expect_column_to_exist",
                "not_null"
            ]
        },
        {
            "name": "is_purged",
            "tests": [
                "dbt_expectations.expect_column_to_exist",
                "not_null"
            ]
        }
    ]
}

моя попытка ниже, но я даже близко к ней не приближаюсь.

df=pd.read_csv('data.csv')
print(df)
nested_dict = df.groupby(['name','columns']).apply(lambda x: x[['tests']].to_dict(orient='records')).to_dict()
 
print(nested_dict)
Почему в 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
0
65
3
Перейти к ответу Данный вопрос помечен как решенный

Ответы 3

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

IIUC, вы можете использовать вложенные вызовы groupby:

out = [{'name': k1, 'columns': [{'name': k2, 'tests': g2['tests'].tolist()}
                                for k2, g2 in g1.groupby('columns')]}
       for k1, g1 in df.groupby('name')]

Поскольку обработка происходит по парам или столбцам, вы также можете представить себе рекурсивный подход:

def group(df, keys):
    if len(keys) > 1:
        key1, key2 = keys[:2]
        return [{key1: k, key2: group(g, keys[1:])}
                for k, g in df.groupby(key1)]
    else:
        return df[keys[0]].tolist()

out = group(df, ['name', 'columns', 'tests'])

Выход:

[{'name': 'ABC_ESTIMATE_REFINED',
  'columns': [{'name': 'cntquota',
               'tests': ['dbt_expectations.expect_column_to_exist', 'not_null']},
              {'name': 'is_purged',
               'tests': ['dbt_expectations.expect_column_to_exist', 'not_null']}],
 }]

Демо рекурсивного подхода с другим порядком ключей:

group(df, ['name', 'tests', 'columns'])

[{'name': 'ABC_ESTIMATE_REFINED',
  'tests': [{'tests': 'dbt_expectations.expect_column_to_exist',
             'columns': ['cntquota', 'is_purged']},
            {'tests': 'not_null', 'columns': ['cntquota', 'is_purged']}],
}]

Что-то вроде:

g = df.groupby(["name", "columns"]).agg({"tests": list})\
      .reset_index().rename(columns = {"name": "index", "columns": "name"})\
      .set_index("index")

result = {
    "name": g.index[0],
    "columns": g.to_dict(orient = "records")
}

Кажется, он выполняет работу для ожидаемого результата в вашем ОП.

Действительно, если существует несколько модальностей name, вам придется вместо этого сохранить его как список словаря:

df = pd.read_csv(io.StringIO("""name,columns,tests
ABC_ESTIMATE_REFINED,cntquota,dbt_expectations.expect_column_to_exist
ABC_ESTIMATE_REFINED,cntquota,not_null
ABC_ESTIMATE_REFINED,is_purged,dbt_expectations.expect_column_to_exist
ABC_ESTIMATE_REFINED,is_purged,not_null
ABC_ESTIMATE_REFINED2,cntquota,dbt_expectations.expect_column_to_exist
ABC_ESTIMATE_REFINED2,cntquota,not_null
ABC_ESTIMATE_REFINED2,is_purged,dbt_expectations.expect_column_to_exist
ABC_ESTIMATE_REFINED2,is_purged,not_null"""))

g = df.groupby(["name", "columns"]).agg({"tests": list})\
      .reset_index().rename(columns = {"name": "index", "columns": "name"})\
      .set_index("index")

result = []
for key in g.index.unique():
    result.append({
        "name": key,
        "columns": g.loc[key,:].to_dict(orient = "records")
    })

Что будет отображать:

[{'name': 'ABC_ESTIMATE_REFINED',
  'columns': [{'name': 'cntquota',
    'tests': ['dbt_expectations.expect_column_to_exist', 'not_null']},
   {'name': 'is_purged',
    'tests': ['dbt_expectations.expect_column_to_exist', 'not_null']}]},
 {'name': 'ABC_ESTIMATE_REFINED2',
  'columns': [{'name': 'cntquota',
    'tests': ['dbt_expectations.expect_column_to_exist', 'not_null']},
   {'name': 'is_purged',
    'tests': ['dbt_expectations.expect_column_to_exist', 'not_null']}]}]

Не будет работать с несколькими значениями в столбце 'name'.

e-motta 19.06.2024 11:39

@e-motta, Можете ли вы сказать нам, как должен выглядеть ожидаемый результат, если имеется несколько имен?

jlandercy 19.06.2024 11:41

Я бы предположил list с dict для каждого 'name'. Но теперь, когда я еще раз проверил вопрос, ОП ожидает один dict, а не list, так что я могу ошибаться.

e-motta 19.06.2024 11:46

Или, может быть, OP и его MCVE недостаточно четко определены.

jlandercy 19.06.2024 11:49

@jlandercy всегда будут уникальные столбцы, и каждый столбец может содержать список тестов. но я получаю TypeError: Series.to_dict() получил неожиданный аргумент ключевого слова 'orient'. Когда я пытаюсь добавить несколько модальностей.

snowflake_user 19.06.2024 12:56
import pandas as pd

# Read the CSV file into a DataFrame
df = pd.read_csv('data.csv')

# Initialize an empty dictionary to hold the final nested structure
nested_dict = {}

# Group by 'name' and 'columns'
grouped = df.groupby(['name', 'columns'])['tests'].apply(list).reset_index()

# Iterate through the grouped DataFrame to construct the nested dictionary
for _, row in grouped.iterrows():
    name = row['name']
    column = row['columns']
    tests = row['tests']

    # If the name is not in the dictionary, add it with an empty 'columns' list
    if name not in nested_dict:
        nested_dict[name] = {'name': name, 'columns': []}

    # Append the column and its tests to the 'columns' list
    nested_dict[name]['columns'].append({'name': column, 'tests': tests})

# Since we have only one 'name', we extract the value from the nested dictionary
result = nested_dict['ABC_ESTIMATE_REFINED']

print(result)

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