Древнее программное обеспечение для моделирования атомов создает действительно уродливый файл CSV, который я хочу импортировать в фрейм данных pandas. Формат выглядит так:
ITEM: TIMESTEP
0
ITEM: NUMBER OF ATOMS
491
ITEM: BOX BOUNDS pp pp pp
0.0000000000000000e+00 2.8000000000000000e+01
0.0000000000000000e+00 2.8000000000000000e+01
0.0000000000000000e+00 2.8000000000000000e+01
ITEM: ATOMS id type xs ys zs
1 1 0 0 0.142857
2 1 0.0714286 0.0714286 0.142857
3 1 0.0714286 0 0.214286
4 1 0 0.0714286 0.214286
...
491 1 2 2.3 0.4
ITEM: TIMESTEP
0
ITEM: NUMBER OF ATOMS
491
ITEM: BOX BOUNDS pp pp pp
0.0000000000000000e+00 2.8000000000000000e+01
0.0000000000000000e+00 2.8000000000000000e+01
0.0000000000000000e+00 2.8000000000000000e+01
ITEM: ATOMS id type xs ys zs
1 1 0 0 0.142857
2 1 0.0714286 0.0714286 0.142857
3 1 0.0714286 0 0.214286
4 1 0 0.0714286 0.214286
...
491 215 0.4 12.4 2.4
...
...
ITEM: TIMESTEP
1002
...
В основном это повторяющийся заголовок с информацией о номере итерации. Мне кажется, что самый простой способ превратить это в панд - это df с переменными ['id', 'type', 'xs', 'ys', 'zs'] и добавить к нему новый столбец "TIMESTEP" , так что это будет хороший 2D df. В качестве альтернативы может быть массив с несколькими индексами Timestep 1 -> internal_df['id', 'type', 'xs', 'ys', 'zs']
Информационные строки (1-9) можно удалить.
Конечный результат в идеале должен выглядеть так:
Index a b c d TIMESTEP
1 1 0 0 0.142857 0
2 1 0.0714286 0.0714286 0.142857 0
3 1 0.0714286 0 0.214286 0
4 1 0 0.0714286 0.214286 0
5 1 0.142857 0 0.142857 0
...
474 1 0.78636 0.788005 0.425791 100002
Не могли бы вы предложить сценарий форматирования строк (пример приветствуется), или, может быть, Pandas read_csv с умным набором настроек может сделать это из коробки?
EDITED: Добавлен истинный бит INFORMATION заголовка, который должен быть отброшен (бит от «Количества записей» до строки «Переменные»)
Почему 1002 находится в той же строке, что и TIMESTEP?
Это не CSV. CSV
конкретно означает Comma Separated Values
, но это выглядит как многостраничный отчет, сохраненный в виде текстового файла.
Предполагая, что вам не нужны никакие данные за пределами таблиц, вы можете попробовать read_table
или, может быть, даже read_csv
и отбросить «плохие» строки, например те, в которых слишком много NaN, чье первое значение не является числом и т. д. Если количество заголовков и строк исправлено, вы можете передать лямбду в skip_rows
, которая отклоняет строки по индексу.
Однако может быть лучше читать и анализировать ваш код и обрабатывать каждый возможный тип строки по-разному.
Panagiotis, разделитель можно легко изменить на ' ' в pandas, но основная проблема - это повторяющийся заголовок с изменением TIMESTEP, ура.
Это не CSV по любому определению. Проблема не в TIMESTEP, а в том, что все, что находится за пределами таблиц, не является таблицей и не может быть обработано таким образом. Даже если бы в первой строке был только один раздел с одним TIMESTEP
, вы все равно не смогли бы использовать read_csv
и получить TIMESTEP
там.
Да, формат данных действительно раздражает, отсюда и вопрос. Хаком было бы, если бы машина сохраняла каждую временную метку в отдельном CSV, тогда начальный заголовок можно было бы игнорировать, а дальше будет просто чистая таблица чисел. Я в основном удалил ненужные строки в Sublime, но это не слишком безопасно, так как теряет информацию о TIMESTEP (конечно, они идут по порядку, но я хочу это автоматизировать).
Это похоже на смесь между строковыми записями и значениями, разделенными пробелами. Вероятно, было бы относительно просто преобразовать с помощью awk и массива 2d. Обновляйте индекс каждый раз, когда видите TIMESTEP
. Затем обработайте строки, которые соответствуют шаблону для фактических данных (1 a1 b1..
). В конце распечатайте массив. Конечно, вы можете сделать то же самое, используя Python.
Предполагая, что вывод будет чем-то таким для проиллюстрированного примера
С пандами, вот один из способов, как вы можете подойти к этому (чтобы дать вам только общую логику).
#pip install pandas
import pandas as pd
import numpy as np
df = pd.read_csv(StringIO(s), sep = "/", header=None)
m1 = df[0].str.contains("TIMESTEP")
m2 = df[0].str.contains("Information").fillna(False)
m3 = df[0].str.contains("ITEM|Variables|\+", regex=True).fillna(False)
conds, vals = [m1|m1.shift(1).fillna(False), m2|m3], ["DATA", "TO_SKIP"]
out = (
df
.assign(flag= pd.Series(np.select(conds, vals, None)).bfill().ffill())
.pivot(columns = "flag", values=0)
.loc[:, "DATA"].dropna()
.str.split(expand=True)
.assign(col= lambda x: x[0].shift(-1).where(x[1].str.contains("TIMESTEP")).ffill())
.set_axis(["Index", "a", "b", "c", "d", "TIMESTEP"], axis=1)
.dropna(how = "any")
.reset_index(drop=True)
)
Выход :
print(out)
Index a b c d TIMESTEP
0 1 1 0 0 0.142857 0
1 2 1 0.0714286 0.0714286 0.142857 0
2 3 1 0.0714286 0 0.214286 0
3 4 1 0 0.0714286 0.214286 0
4 491 1 2 2.3 0.4 0
5 1 1 0 0 0.142857 0
6 2 1 0.0714286 0.0714286 0.142857 0
7 3 1 0.0714286 0 0.214286 0
8 4 1 0 0.0714286 0.214286 0
9 491 215 0.4 12.4 2.4 0
Отказ от ответственности: этот подход может быть неэффективным для больших файлов.
Вы должны добавить это в свой вопрос, потому что это нечитабельно в разделе комментариев.
Отлично! Я добавил больше деталей в «Информационные строки», но пытаюсь использовать ваше решение для их удаления :) Также спасибо Timeless за предложение отредактировать вопрос OG.
Нам просто нужно добавить |\+
в третью маску, поэтому мы пропускаем строки, содержащие символ +. Я обновил свой ответ, пожалуйста, надеюсь, это поможет;)
Я много изучаю... Что делает этот оператор регулярного выражения? Когда я пытаюсь запустить свои данные, я получаю: ValueError: Length mismatch: Expected axis has 8 elements, new values have 7 elements
. Что, вероятно, означает, что у меня либо неправильное условие, либо какое-то несоответствие столбцов...
Кажется, есть один дополнительный столбец. Можете ли вы прокомментировать (#
) строку, где мы вызываем set_index
, перезапустить код и поделиться результатом (в виде скриншота)?
Извините, вы имеете в виду set_axis?
Мой плохой, да set_axis
, можешь сделать это, пожалуйста?
Давайте продолжим обсуждение в чате.
@GedasSarpis, тебе удалось сделать скриншот?
Я написал вам в чат, так как обсуждение расширяется
Как должен выглядеть ваш красивый 2D df? Можете ли вы добавить это к своему вопросу, пожалуйста?