У меня есть файл данных, который содержит следующее:
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]}
С входным файлом:
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, я очищу всю историю в этом сообщении и иметь более простой ответ.
Надеюсь это поможет! :)
Я бы избегал панд, просто потому, что я не очень хорошо это знаю:
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:])