#python #json #pandas
#python #json #панды
Вопрос:
Я не уверен, что этой информации будет достаточно для предоставления, но в настоящее время я пытаюсь извлечь и отформатировать в csv подмножество данных из очень большого файла, содержащего много объектов JSON в виде строк, и выгрузить его в один csv-файл. У меня есть приведенная ниже реализация. Скорость не так уж и плоха, но мне было интересно, есть ли более эффективный способ сделать это. Я чувствую, что часть pandas, в которой я создаю фреймы данных, может быть немного приятнее:
for files in zip_files:
with zipfile.ZipFile(files, 'r') as myzip:
for logfile in myzip.namelist():
list1 = []
list2 = []
f = myzip.open(logfile)
contents = f.readlines()
for line in contents[:]:
try:
parsed = json.loads(line[:-2])
if "key1" in parsed.keys():
if "val1" in parsed['key1']['key2']:
if "val2" in parsed['key3']:
list1.append(parsed['key1'])
list2.append(parsed['key3'])
except ValueError as e:
pass
else:
pass
df1 = pd.DataFrame(list1)
df2 = pd.DataFrame(list2)
df3 = df2.join(df1)
df3['col1'] = df3['col1'].apply(lambda x: ','.join([str(i) for i in x]))
df3 = df3.drop_duplicates()
with open(csvout, 'a') as f2:
df.to_csv(f2, header=None, index=False)
f2.close()
f.close()
Комментарии:
1. Вы могли бы попробовать и использовать
read_json
andconcat
, который может быть более эффективным. На данный момент в этом вопросе слишком много всего происходит, на что сложно ответить.2. Этот вопрос, похоже, не по теме, потому что речь идет об улучшении рабочего кода
3. Спасибо. Не знаком с правилами для вопросов. Есть ли ссылка, которую вы можете предоставить?
Ответ №1:
Я сделал следующее:
- Аннотировал исходную версию
STRANGE
(= Я не уверен, что вы делаете),EFFICIENT
(= можно сделать более эффективным),SIMPLIFY
(= можно упростить). - Созданы две другие версии, которые могут быть более эффективными, но изменяют поведение (с точки зрения памяти или поведения выполнения). Это подробно описано ниже.
Чтобы убедиться, что эти предложения действительно полезны, рассмотрите возможность использования ipython %timeit
. Введите следующее в приглашении IPython:
In [0]: %timeit -n<N> %run script.py
Где <N>
среднее количество запусков (по умолчанию 1000, что может занять слишком много времени).
Аннотированный оригинал
for files in zip_files:
with zipfile.ZipFile(files, 'r') as myzip:
for logfile in myzip.namelist():
list1 = []
list2 = []
f = myzip.open(logfile)
# contents = f.readlines()
# for line in contents[:]:
for line in f: # EFFICIENT: does the same without making a copy
try:
parsed = json.loads(line[:-2])
# if "key1" in parsed.keys():
if "key1" in parsed: # EFFICIENT: no copy
# STRANGE: 'val' in dict checks for key existence by
# default, are you sure this is what you want?
if "val1" in parsed['key1']['key2']:
if "val2" in parsed['key3']:
list1.append(parsed['key1'])
list2.append(parsed['key3'])
except ValueError as e:
pass
# STRANGE: Why is this here?
# else:
# pass
df1 = pd.DataFrame(list1)
df2 = pd.DataFrame(list2)
df3 = df2.join(df1)
# EFFICIENT: prefer generator over list comprehension
# df3['col1'] = df3['col1'].apply(lambda x: ','.join([str(i) for i in x]))
df3['col1'] = df3['col1'].apply(lambda x: ','.join(str(i) for i in x))
df3.drop_duplicates(inplace=True)
# SIMPLIFY:
# with open(csvout, 'a') as f2:
# df.to_csv(f2, header=None, index=False)
# f2.close()
# STRANGE: where does `df` come from? Shouldn't this be df3?
df.to_csv(csvout, mode='a', header=None, index=False)
# STRANGE: you open f in a loop, but close it outside of the loop?
f.close()
Сборка в памяти, запись один раз
Если у вас достаточно памяти, быстрее может быть следующее: вместо добавления к файлу вы сначала объединяете все файлы в памяти. Это также немного меняет поведение:
- дубликаты фильтруются по всем файлам
Также некоторые стилистические изменения:
for files in zip_files:
with zipfile.ZipFile(files, 'r') as myzip:
list1, list2 = [], [] # Notice these are outside the loop
for logfile in myzip.namelist():
with myzip.open(logfile) as f:
for line in f:
try:
parsed = json.loads(line[:-2])
except ValueError as e: # Presumably we only wish to catch json value errors
pass
else:
if ("key1" in parsed
and "val1" in parsed['key1']['key2']
and "val2" in parsed['key3']):
list1.append(parsed['key1'])
list2.append(parsed['key3'])
# Write only once
df = pd.DataFrame(list2).join(pd.DataFrame(list1))
df['col1'] = df['col1'].apply(lambda x: ','.join(str(i) for i in x))
df.drop_duplicates(inplace=True)
df.to_csv(csvout, header=None, index=False)
Сборка в памяти, запись один раз, фильтрация дубликатов только для каждого файла
Сохранение локальной фильтрации дубликатов для каждого файла:
for files in zip_files:
with zipfile.ZipFile(files, 'r') as myzip:
dfs = []
for logfile in myzip.namelist():
list1, list2 = [], []
with myzip.open(logfile) as f:
for line in f:
try:
parsed = json.loads(line[:-2])
except ValueError as e: # Presumably we only wish to catch json value errors
pass
else:
if ("key1" in parsed
and "val1" in parsed['key1']['key2']
and "val2" in parsed['key3']):
list1.append(parsed['key1'])
list2.append(parsed['key3'])
# Build a temporary dataframe to filter the duplicates:
tmp = pd.DataFrame(list2).join(pd.DataFrame(list1))
tmp['col1'] = tmp['col1'].apply(lambda x: ','.join(str(i) for i in x))
tmp.drop_duplicates(inplace=True)
dfs.append(tmp)
# Write only once
pd.concat(dfs, ignore_index=True).to_csv(csvout, header=None, index=False)