Как получить словарь словарей, который хранит агрегированные значения из CSV-файла

1

У меня есть файл данных, который содержит следующее:

 Part#1
         A 10 20 10 10 30 10 20 10 30 10 20
         B 10 10 20 10 10 30 10 30 10 20 30
  Part#2
         A 30 30 30 10 10 20 20 20 10 10 10
         B 10 10 20 10 10 30 10 30 10 30 10
  Part#3
         A 10 20 10 30 10 20 10 20 10 20 10
         B 10 10 20 20 20 30 10 10 20 20 30

Оттуда я ищу словарь словарей с суммированными данными на письмо, поэтому это будет примерно так:

dictionary = {{Part#1:{A:{10:6, 20:3, 30:2},
                       B:{10:6, 20:2, 30:3}}}, 
              {Part#2:{A:{10:5, 20:3, 30:3}, 
                       B:{10:7, 20:1, 30:3}}}, 
              {Part#3:{A:{10:6, 20:4, 30:1}, 
                       B:{10:4, 20:5, 30:2}}}} 

таким образом, если я хочу отображать каждую часть, он даст мне такой вывод:

dictionary[Part#1]

A
 10: 6
 20: 3
 30: 2

B
 10: 6
 20: 2
 30: 3

... и т.д. Для следующих двух разделов в файле.

На данный момент мне удалось проанализировать файл с txt на csv. и преобразовать их в словарь, скажем, внешний словарь. Я тестировал несколько способов увидеть результат, который я получаю, и до сих пор этот фрагмент кода ближе (но не полностью) к структуре, которую я ищу, о которой я уже говорил выше.

partitions_dict = df_head(5).to_dict(orient='list')      

print(partitions_dict)

Output:

{0: ['A', 'B', 'A', 'B', 'A'], 1: ['10', '10', '10', '10', '10'], 2: [10, 10, 10, 10, 10], 3: [10, 10, 10, 10, 10], 4: [10, 10, 10, 10, 10], 5: [10, 10, 10, 10, 10], 6: [10, 10, 10, 10, 10], 7: [10, 10, 10, 10, 10]

Функции, которые я использую для анализа файла:

def fileFormatConverter(txt_file):
    """ Receives a generated text file  of partitions as a parameter
        and converts it into csv format.
        input: text file
        return: csv file """

    filename, ext = os.path.splitext(txt_file)
    csv_file = filename + ".csv"
    in_txt = csv.reader(open(txt_file, "r"), delimiter = ' ')
    out_csv = csv.writer(open(csv_file,'w'))
    out_csv.writerows(in_txt)   
    return (csv_file)

# removes "Part#0" as a header from the dataframe
df_traces = pd.read_csv(fileFormatConverter("sample.txt"), skiprows=1, header=None)   #, error_bad_lines=False)
df_traces.head()

выход:

    0   1   2   3   4   5   6   7   8   9   ...     15  16  17  18  19  20  21  22  23  24
0   A,  10,     20,     10,     10,     30,     10,     20,     10,     30,     ...     20,     10,     10,     30,     10,     30,     10,     20,     30.0    NaN
1   Part#2  NaN     NaN     NaN     NaN     NaN     NaN     NaN     NaN     NaN     ...     NaN     NaN     NaN     NaN     NaN     NaN     NaN     NaN     NaN     NaN
2   A,  30,     30,     30,     10,     10,     20,     20,     20,     10,     ...     20,     10,     10,     30,     10,     30,     10,     30,     10.0    NaN
3   Part#3  NaN     NaN     NaN     NaN     NaN     NaN     NaN     NaN     NaN     ...     NaN     NaN     NaN     NaN     NaN     NaN     NaN     NaN     NaN     NaN
4   A,  10,     20,     10,     30,     10,     20,     10,     20,     10,     ...     20,     20,     20,     30,     10,     10,     20,     20,     30.0    NaN

Я использовал функцию для изменения заголовков, чтобы было легче манипулировать буквами внутри каждого раздела:

def changeDFHeaders(df):

    df_transpose = df.T
    new_header = df_transpose.iloc[0]                       # stores the first row for the header
    df_transpose = df_transpose[1:]                         # take the data less the header row
    df_transpose.columns = new_header                       # set the header row as the df header
    return(df_transpose)


# The counter column serves as an index for the entire dataframe
#df_transpose['counter'] = range(len(df_transpose))      # adds the counter for rows column
#df_transpose.set_index('counter', inplace=True)
df_transpose_headers = changeDFHeaders(df_traces)
df_transpose_headers.infer_objects()

Выход:

    A,  Part#2  A,  Part#3  A,
1   10,     NaN     30,     NaN     10,
2   20,     NaN     30,     NaN     20,
3   10,     NaN     30,     NaN     10,
4   10,     NaN     10,     NaN     30,
5   30,     NaN     10,     NaN     10,
6   10,     NaN     20,     NaN     20,
7   20,     NaN     20,     NaN     10,
8   10,     NaN     20,     NaN     20,
9   30,     NaN     10,     NaN     10,
10  10,     NaN     10,     NaN     20,
11  20,     NaN     10,     NaN     10,
12  B,  NaN     B,  NaN     B,
13  10,     NaN     10,     NaN     10,
14  10,     NaN     10,     NaN     10,
15  20,     NaN     20,     NaN     20,
16  10,     NaN     10,     NaN     20,
17  10,     NaN     10,     NaN     20,
18  30,     NaN     30,     NaN     30,
19  10,     NaN     10,     NaN     10,
20  30,     NaN     30,     NaN     10,
21  10,     NaN     10,     NaN     20,
22  20,     NaN     30,     NaN     20,
23  30  NaN     10  NaN     30
24  NaN     NaN     NaN     NaN     NaN

--still не совсем прав...

и если вы проверите это утверждение:

df = df_transpose_headers
partitions_dict = df.head(5).to_dict(orient='list')      

print(partitions_dict) 

выход:

{'A,': ['10,', '20,', '10,', '30,', '10,'], 'Part#2': [nan, nan, nan, nan, nan], 'Part#3': [nan, nan, nan, nan, nan]}
  • 0
    Я заметил, что вы отредактировали свой вопрос, чтобы уточнить, почему это не дубликат: можете ли вы также отредактировать, чтобы включить то, что вы пытались решить эту проблему? Пожалуйста, включите весь соответствующий код, который у вас есть.
  • 1
    @TemporalWolf спасибо за предложение!
Показать ещё 3 комментария
Теги:
dictionary
aggregate
nested
summary

2 ответа

0
Лучший ответ

С входным файлом:

  Part#1
         A 10 20 10 10 30 10 20 10 30 10 20
         B 10 10 20 10 10 30 10 30 10 20 30
  Part#2
         A 30 30 30 10 10 20 20 20 10 10 10
         B 10 10 20 10 10 30 10 30 10 30 10
  Part#3
         A 10 20 10 30 10 20 10 20 10 20 10
         B 10 10 20 20 20 30 10 10 20 20 30

Это должно работать

def parse_file(file_name):
    return_dict = dict()
    section = str()
    with open(file_name, "r") as source:
        for line in source.readlines():
            if "#" in line:
                section = line.strip()
                return_dict[section] = dict()
                continue
            tmp = line.strip().split()
            group = tmp.pop(0)
            return_dict[section][group] = dict()
            for item in tmp:
                if item in return_dict[section][group].keys():
                    return_dict[section][group][item] += 1
                else:
                    return_dict[section][group][item] = 1

    return return_dict

выходы

{'Part#1': {'A': {'10': 6, '20': 3, '30': 2},
            'B': {'10': 6, '20': 2, '30': 3}},
 'Part#2': {'A': {'10': 5, '20': 3, '30': 3},
            'B': {'10': 7, '20': 1, '30': 3}},
 'Part#3': {'A': {'10': 6, '20': 4, '30': 1},
            'B': {'10': 4, '20': 5, '30': 2}}}

Я честно не понимаю, почему вам нужен промежуточный этап, похоже, если вам нужно разобрать файл один раз, чтобы создать CSV, вы можете просто добавить свою логику для создания своего dict(). Поэтому, если я пропустил какую-то тонкость в вопросе, я прошу прощения.

Изменение: переформулированный ответ на основе комментариев, что входной файл на самом деле является одной строкой

поэтому с входным файлом

Part#1 A 10 20 10 10 30 10 20 10 30 10 20 B 10 10 20 10 10 30 10 30 10 20 30 Part#2 A 30 30 30 10 10 20 20 20 10 10 10 B 10 10 20 10 10 30 10 30 10 30 10 Part#3 A 10 20 10 30 10 20 10 20 10 20 10 B 10 10 20 20 20 30 10 10 20 20 30

Следующий модифицированный код будет работать

import string
from pprint import pprint

def parse_file2(file_name):
    return_dict = dict()
    section = None
    group = None
    with open(file_name, "r") as source:
        for line in source.readlines():
            tmp_line = line.strip().split()
            for token in tmp_line:
                if "#" in token:
                    section = token
                    return_dict[section] = dict()
                    continue
                elif token in string.ascii_uppercase:
                    group = token
                    return_dict[section][group] = dict()
                    continue
                if section and group:
                    if token in return_dict[section][group].keys():
                        return_dict[section][group][token] += 1
                    else:
                        return_dict[section][group][token] = 1

    return return_dict

if __name__ == "__main__":
    pprint(parse_file(file_name))
    pprint(parse_file2(file_name2))

Обратите внимание, что эта функция специально привязана к формату файла, который вы отметили в комментариях. Если формат файла не такой, как вы говорите, он, вероятно, взорвется.

Исходя из проблемы, хотя это должно сработать.

Кроме того, если вы можете упростить поставленный выше вопрос, чтобы просто проиллюстрировать фактическое содержимое файла и желаемые результаты, или просто добавить его, у меня есть структура A и вы хотите преобразовать ее в структуру B, я очищу всю историю в этом сообщении и иметь более простой ответ.

Надеюсь это поможет! :)

  • 0
    потому что по какой-то причине, если я делаю это напрямую, файл воспринимается как объект (весь файл - один объект) и содержит более 50 тыс. записей, поэтому его нельзя вызывать напрямую как текстовый файл. делать функцию напрямую, и это не сработало. Я ценю вашу помощь, это огромная помощь ... Спасибо :)!
  • 0
    Хм, значит ли это, что в вашем файле нет символов новой строки? То, что вы описываете, должно иметь место, если вы используете read (), а не readlines (). Кроме того, в вопросе неясно, является ли ваша промежуточная стадия правильной. В вашей функции partition_dict похоже, что все ваши списки содержат только 10 (кроме первого, который содержит повторяющийся шаблон A, B. Если вы ищете средство для преобразования промежуточной структуры в конечную структуру, она может удалить некоторые путаница, чтобы просто проиллюстрировать. Например: «У меня есть структура A, и я хочу превратить ее в эту структуру B»
Показать ещё 3 комментария
2

Я бы избегал панд, просто потому, что я не очень хорошо это знаю:

from collections import Counter

result = {}
part = ""
group = ""
for line in f:  # f being an open file
    sline = line.strip()
    if sline.startswith("Part"):
        part = sline
        result[part] = {}
        continue
    group = sline.split()[0]
    result[part][group] = Counter(sline.split()[1:])

Результат принимает форму:

{'Part#1': {'A': Counter({'10': 6, '20': 3, '30': 2}), 'B': Counter({'10': 6, '30': 3, '20': 2})}, 
 'Part#2': {'A': Counter({'10': 5, '30': 3, '20': 3}), 'B': Counter({'10': 7, '30': 3, '20': 1})}, 
 'Part#3': {'A': Counter({'10': 6, '20': 4, '30': 1}), 'B': Counter({'20': 5, '10': 4, '30': 2})}}

Если вы переходите напрямую из файла, который не отделен от линии, вы можете использовать "Part" для поиска строк, а затем использовать индекс "B" для разделения двух типов данных:

result = {}
sf = f.split("Part")[1:]  # drop the empty first part
for line in sf:
    line = line.strip()  # remove trailing spaces
    sline = line.split()  # split on spaces
    result["Part%s" % sline[0]] = {}  # Use the index of B to split the value lists
    result["Part%s" % sline[0]][sline[1]] = Counter(sline[2:sline.index("B")])
    result["Part%s" % sline[0]]["B"] = Counter(sline[sline.index("B") + 1:])
  • 0
    Спасибо! Я использую панды в основном для преобразования файла из TXT в CSV. дисплей предназначен для проверки выходов.
  • 0
    Я добавил другой метод, который должен работать непосредственно из файла, если он состоит из одной гигантской строки. Если существует более двух типов (A / B), то для общего среза его необходимо абстрагировать.
Показать ещё 1 комментарий

Ещё вопросы

Сообщество Overcoder
Наверх
Меню