Вот проблема, с которой я столкнулся:
Я хочу построить график некоторых энергетических полос, полученных с помощью Quantum Espresso. Данные поступают в файл с двумя столбцами. Столбцы разделены пустыми строками на блоки. Каждый блок соответствует полосе.
Вот пример первых двух блоков:
0.0000 -44.2709
0.0250 -44.2709
0.0500 -44.2709
0.0750 -44.2708
0.1000 -44.2708
0.1250 -44.2707
0.1500 -44.2706
0.1750 -44.2705
0.2000 -44.2703
0.2250 -44.2702
0.2500 -44.2701
0.2750 -44.2700
0.3000 -44.2698
0.3250 -44.2697
0.3500 -44.2696
0.3750 -44.2695
0.4000 -44.2694
0.4250 -44.2694
0.4500 -44.2693
0.4750 -44.2693
0.5000 -44.2693
0.5250 -44.2693
0.5500 -44.2692
0.5750 -44.2692
0.6000 -44.2691
0.6250 -44.2690
0.6500 -44.2689
0.6750 -44.2688
0.7000 -44.2687
0.7250 -44.2686
0.7500 -44.2685
0.7750 -44.2683
0.8000 -44.2682
0.8250 -44.2681
0.8500 -44.2680
0.8750 -44.2679
0.9000 -44.2678
0.9250 -44.2678
0.9500 -44.2677
0.9750 -44.2677
1.0000 -44.2677
1.0354 -44.2677
1.0707 -44.2677
1.1061 -44.2678
1.1414 -44.2680
1.1768 -44.2681
1.2121 -44.2683
1.2475 -44.2686
1.2828 -44.2688
1.3182 -44.2690
1.3536 -44.2693
1.3889 -44.2695
1.4243 -44.2698
1.4596 -44.2700
1.4950 -44.2702
1.5303 -44.2704
1.5657 -44.2706
1.6010 -44.2707
1.6364 -44.2708
1.6718 -44.2709
1.7071 -44.2709
1.7504 -44.2709
1.7937 -44.2708
1.8370 -44.2706
1.8803 -44.2704
1.9236 -44.2702
1.9669 -44.2699
2.0102 -44.2696
2.0535 -44.2692
2.0968 -44.2689
2.1401 -44.2685
2.1834 -44.2681
2.2267 -44.2677
2.2700 -44.2674
2.3133 -44.2671
2.3566 -44.2668
2.3999 -44.2665
2.4432 -44.2663
2.4865 -44.2662
2.5298 -44.2661
2.5731 -44.2661
2.6085 -44.2661
2.6438 -44.2661
2.6792 -44.2662
2.7146 -44.2664
2.7499 -44.2665
2.7853 -44.2667
2.8206 -44.2669
2.8560 -44.2672
2.8913 -44.2674
2.9267 -44.2677
2.9620 -44.2679
2.9974 -44.2682
3.0328 -44.2684
3.0681 -44.2686
3.1035 -44.2688
3.1388 -44.2690
3.1742 -44.2691
3.2095 -44.2692
3.2449 -44.2693
3.2802 -44.2693
3.2802 -44.2677
3.3052 -44.2677
3.3302 -44.2676
3.3552 -44.2676
3.3802 -44.2675
3.4052 -44.2674
3.4302 -44.2673
3.4552 -44.2672
3.4802 -44.2671
3.5052 -44.2670
3.5302 -44.2669
3.5552 -44.2667
3.5802 -44.2666
3.6052 -44.2665
3.6302 -44.2664
3.6552 -44.2663
3.6802 -44.2662
3.7052 -44.2662
3.7302 -44.2661
3.7552 -44.2661
3.7802 -44.2661
0.0000 -20.8317
0.0250 -20.8322
0.0500 -20.8338
0.0750 -20.8364
0.1000 -20.8400
0.1250 -20.8445
0.1500 -20.8497
0.1750 -20.8555
0.2000 -20.8618
0.2250 -20.8684
0.2500 -20.8751
0.2750 -20.8819
0.3000 -20.8884
0.3250 -20.8947
0.3500 -20.9004
0.3750 -20.9055
0.4000 -20.9098
0.4250 -20.9133
0.4500 -20.9159
0.4750 -20.9174
0.5000 -20.9179
0.5250 -20.9179
0.5500 -20.9178
0.5750 -20.9175
0.6000 -20.9172
0.6250 -20.9169
0.6500 -20.9164
0.6750 -20.9159
0.7000 -20.9154
0.7250 -20.9149
0.7500 -20.9143
0.7750 -20.9137
0.8000 -20.9132
0.8250 -20.9126
0.8500 -20.9122
0.8750 -20.9117
0.9000 -20.9113
0.9250 -20.9110
0.9500 -20.9108
0.9750 -20.9107
1.0000 -20.9106
1.0354 -20.9102
1.0707 -20.9089
1.1061 -20.9068
1.1414 -20.9039
1.1768 -20.9003
1.2121 -20.8959
1.2475 -20.8910
1.2828 -20.8855
1.3182 -20.8797
1.3536 -20.8736
1.3889 -20.8673
1.4243 -20.8611
1.4596 -20.8551
1.4950 -20.8495
1.5303 -20.8444
1.5657 -20.8400
1.6010 -20.8365
1.6364 -20.8338
1.6718 -20.8322
1.7071 -20.8317
1.7504 -20.8322
1.7937 -20.8338
1.8370 -20.8365
1.8803 -20.8400
1.9236 -20.8443
1.9669 -20.8492
2.0102 -20.8545
2.0535 -20.8601
2.0968 -20.8659
2.1401 -20.8716
2.1834 -20.8772
2.2267 -20.8826
2.2700 -20.8876
2.3133 -20.8922
2.3566 -20.8962
2.3999 -20.8997
2.4432 -20.9025
2.4865 -20.9045
2.5298 -20.9058
2.5731 -20.9062
2.6085 -20.9063
2.6438 -20.9064
2.6792 -20.9067
2.7146 -20.9071
2.7499 -20.9076
2.7853 -20.9082
2.8206 -20.9089
2.8560 -20.9096
2.8913 -20.9105
2.9267 -20.9114
2.9620 -20.9123
2.9974 -20.9132
3.0328 -20.9142
3.0681 -20.9151
3.1035 -20.9159
3.1388 -20.9166
3.1742 -20.9171
3.2095 -20.9176
3.2449 -20.9178
3.2802 -20.9179
3.2802 -20.9106
3.3052 -20.9106
3.3302 -20.9105
3.3552 -20.9104
3.3802 -20.9102
3.4052 -20.9100
3.4302 -20.9097
3.4552 -20.9094
3.4802 -20.9091
3.5052 -20.9088
3.5302 -20.9084
3.5552 -20.9081
3.5802 -20.9078
3.6052 -20.9074
3.6302 -20.9071
3.6552 -20.9069
3.6802 -20.9066
3.7052 -20.9065
3.7302 -20.9063
3.7552 -20.9063
3.7802 -20.9062
Вы можете заметить, что первый столбец снова и снова содержит одни и те же данные, и только второй столбец содержит разные данные. Я хотел бы сохранить только первый столбец из первого блока, а затем преобразовать в отдельные столбцы второй столбец. Так:
0.0000 -44.2709 -20.8317
0.0250 -44.2709 -20.8322
0.0500 -44.2709 -20.8338
0.0750 -44.2708 -20.8364
0.1000 -44.2708 -20.8400
0.1250 -44.2707 -20.8445
0.1500 -44.2706 -20.8497
0.1750 -44.2705 -20.8555
0.2000 -44.2703 -20.8618
0.2250 -44.2702 -20.8684
0.2500 -44.2701 -20.8751
0.2750 -44.2700 -20.8819
0.3000 -44.2698 -20.8884
0.3250 -44.2697 -20.8947
0.3500 -44.2696 -20.9004
0.3750 -44.2695 -20.9055
0.4000 -44.2694 -20.9098
0.4250 -44.2694 -20.9133
0.4500 -44.2693 -20.9159
0.4750 -44.2693 -20.9174
0.5000 -44.2693 -20.9179
0.5250 -44.2693 -20.9179
0.5500 -44.2692 -20.9178
0.5750 -44.2692 -20.9175
0.6000 -44.2691 -20.9172
0.6250 -44.2690 -20.9169
0.6500 -44.2689 -20.9164
0.6750 -44.2688 -20.9159
0.7000 -44.2687 -20.9154
0.7250 -44.2686 -20.9149
0.7500 -44.2685 -20.9143
0.7750 -44.2683 -20.9137
0.8000 -44.2682 -20.9132
0.8250 -44.2681 -20.9126
0.8500 -44.2680 -20.9122
0.8750 -44.2679 -20.9117
0.9000 -44.2678 -20.9113
0.9250 -44.2678 -20.9110
0.9500 -44.2677 -20.9108
0.9750 -44.2677 -20.9107
1.0000 -44.2677 -20.9106
1.0354 -44.2677 -20.9102
1.0707 -44.2677 -20.9089
1.1061 -44.2678 -20.9068
1.1414 -44.2680 -20.9039
1.1768 -44.2681 -20.9003
1.2121 -44.2683 -20.8959
1.2475 -44.2686 -20.8910
1.2828 -44.2688 -20.8855
1.3182 -44.2690 -20.8797
1.3536 -44.2693 -20.8736
1.3889 -44.2695 -20.8673
1.4243 -44.2698 -20.8611
1.4596 -44.2700 -20.8551
1.4950 -44.2702 -20.8495
1.5303 -44.2704 -20.8444
1.5657 -44.2706 -20.8400
1.6010 -44.2707 -20.8365
1.6364 -44.2708 -20.8338
1.6718 -44.2709 -20.8322
1.7071 -44.2709 -20.8317
1.7504 -44.2709 -20.8322
1.7937 -44.2708 -20.8338
1.8370 -44.2706 -20.8365
1.8803 -44.2704 -20.8400
1.9236 -44.2702 -20.8443
1.9669 -44.2699 -20.8492
2.0102 -44.2696 -20.8545
2.0535 -44.2692 -20.8601
2.0968 -44.2689 -20.8659
2.1401 -44.2685 -20.8716
2.1834 -44.2681 -20.8772
2.2267 -44.2677 -20.8826
2.2700 -44.2674 -20.8876
2.3133 -44.2671 -20.8922
2.3566 -44.2668 -20.8962
2.3999 -44.2665 -20.8997
2.4432 -44.2663 -20.9025
2.4865 -44.2662 -20.9045
2.5298 -44.2661 -20.9058
2.5731 -44.2661 -20.9062
2.6085 -44.2661 -20.9063
2.6438 -44.2661 -20.9064
2.6792 -44.2662 -20.9067
2.7146 -44.2664 -20.9071
2.7499 -44.2665 -20.9076
2.7853 -44.2667 -20.9082
2.8206 -44.2669 -20.9089
2.8560 -44.2672 -20.9096
2.8913 -44.2674 -20.9105
2.9267 -44.2677 -20.9114
2.9620 -44.2679 -20.9123
2.9974 -44.2682 -20.9132
3.0328 -44.2684 -20.9142
3.0681 -44.2686 -20.9151
3.1035 -44.2688 -20.9159
3.1388 -44.2690 -20.9166
3.1742 -44.2691 -20.9171
3.2095 -44.2692 -20.9176
3.2449 -44.2693 -20.9178
3.2802 -44.2693 -20.9179
3.2802 -44.2677 -20.9106
3.3052 -44.2677 -20.9106
3.3302 -44.2676 -20.9105
3.3552 -44.2676 -20.9104
3.3802 -44.2675 -20.9102
3.4052 -44.2674 -20.9100
3.4302 -44.2673 -20.9097
3.4552 -44.2672 -20.9094
3.4802 -44.2671 -20.9091
3.5052 -44.2670 -20.9088
3.5302 -44.2669 -20.9084
3.5552 -44.2667 -20.9081
3.5802 -44.2666 -20.9078
3.6052 -44.2665 -20.9074
3.6302 -44.2664 -20.9071
3.6552 -44.2663 -20.9069
3.6802 -44.2662 -20.9066
3.7052 -44.2662 -20.9065
3.7302 -44.2661 -20.9063
3.7552 -44.2661 -20.9063
3.7802 -44.2661 -20.9062
Но есть загвоздка! Мне удалось сделать что-то близкое к numpy.unique, но я заметил, что по какой-то причине Quantum Espresso иногда записывает два или более одинаковых значения в блоках первого столбца, в то время как соответствующие значения во втором столбце различаются, и при использовании numpy.uniques я потеряю данные .
Я пробовал так: kp_bands=np.take(bands[:,0],range(0,122),axis=0). Здесь bands — это место, куда я загрузил данные с помощью numpy.loadtxt, а 122 — количество значений в каждом блоке. Проблема в том, что это не всегда одно и то же. Может быть разным в зависимости от изучаемой системы.
Мой вопрос:
Как я могу это сделать, не теряя данные и не зная, сколько строк в каждом блоке?
Я действительно начинаю работать с Python и сначала пробовал панды, но, похоже, я просто не понимаю, как с ним работать. numpy выглядит более управляемым. Я действительно пытаюсь создать несколько программ для просмотра моих данных, и мне придется обработать много данных.
С такими структурированными данными pandas действительно лучший выбор
можете ли вы привести пример, пожалуйста?






как уже упоминалось в комментариях, панды действительно лучший выбор здесь
import pandas as pd
import numpy as np
def read_arrays(filename):
arrays = []
with open(filename,"r") as f:
data = f.read().split("\n\n")
for item in data:
arr = np.fromstring(item,dtype=float,sep = " ")
arr = arr.reshape(len(arr)//2,2)
arrays.append(arr)
return arrays
def all_to_df(numpy_arrays):
return [
pd.DataFrame(data=item) for item in numpy_arrays
]
data = read_arrays("temp.txt")
dataframes = all_to_df(data)
#some check
print(dataframes[0])
print(dataframes[1])
df = dataframes[0] #assuming at least 1 dataframe
for i in range(1,len(dataframes)):
df = pd.merge(df,dataframes[i], on=0)
df = df.rename(columns = {"0": 'timestamp',"1_x": 'value_1',"1_y": 'value_2'})
print(df)
# or use this for printing without truncating
# with pd.option_context('display.max_rows', None,
# 'display.max_columns', None,
# ):
# print(df)
# converting to csv is also an option
# https://pandas.pydata.org/pandas-docs/stable/reference/api/pandas.DataFrame.to_csv.html
Проверьте df[df[0] == 3.2802] и обнаружите, что удвоенные данные были возвращены как декартово произведение, т. е. имеется 4 вместо 2 записей с 3,2802.
хорошая находка, хотя я правда не знаю, как это решить
Допустим, мы знаем, что данные организованы в виде ряда блоков, расположенных вертикально, где каждый блок состоит из двух столбцов, а первый столбец отсортирован [например, в порядке возрастания] и одинаков для всех блоков. Затем мы можем использовать numpy.diff, чтобы найти количество блоков и numpy.split, чтобы разделить их:
bands = np.loadtxt(file_name)
number_of_bands = 1 + np.sum(np.diff(bands[:,0]) < 0) # or 1 + sum(bands[1:, 0] < bands[:-1, 0])
values = np.split(bands[:,1], number_of_bands)
band_len = values[0].shape[0]
index = bands[:band_len, 0]
result = np.array([index, *values]).T # F_CONTIGUOUS
# or
result = np.c_[index, *values] # C_CONTIGUOUS
Допустим, мы хотим обработать данные следующего шаблона:
data = '''\
0.0000 -44.2709
0.0500 -44.2709
0.0750 -44.2701
0.0750 -44.2702
0.1000 -44.2708
0.1250 -44.2707
0.1500 -44.2706
0.0750 -44.2703
0.0000 -20.8317
0.0250 -20.8322
0.0500 -20.8338
0.0750 -20.8364
0.0750 -20.8365
0.1000 -20.8400
0.1000 -20.8401
0.1250 -20.8445
'''
Мы знаем, что есть вертикально расположенные блоки, разделенные пустой строкой. А данные в первом столбце блоков перекрываются, но могут не сортироваться и не совпадать.
Для работы с ними я бы использовал Панды . Мы можем прочитать их с помощью pandas.read_csv(file, sep='\s+', ...), но я собираюсь использовать здесь более специализированный метод pandas.read_fwf:
import pandas as pd
from io import StringIO
file = StringIO(data)
first, second = 'first column', 'second column'
df = pd.read_fwf(file, names=[first, second], skip_blank_lines=False)
Что важно, мы оставляем пустые строки с параметром skip_blank_lines=False, чтобы сгруппировать данные на следующем шаге:
df['block'] = df[first].isna().cumsum() # assign a unique number to each block
df = df.dropna() # drop empty lines
Теперь идея состоит в том, чтобы использовать один из методов поворота, используя первый столбец в качестве индекса и номера блоков в качестве имен столбцов. Но мы не сможем этого сделать, если все пары (индекс, столбец) не уникальны. В вашем случае данные в первом столбце могут дублироваться внутри блока. Поэтому нам нужно сначала это исправить. Например, нумеруя повторяющиеся значения в блоке:
df['inner_id'] = df.groupby(['block', first]).cumcount()
И теперь мы собираем сводные данные, например:
result = df.pivot(columns='block', index=[first, 'inner_id'], values=second)
И при необходимости сохраните их как массив Numpy:
result = result.reset_index(first).to_numpy()
Код для экспериментов:
import pandas as pd
from io import StringIO
# test data, note the difference between blocks in the first column
data = '''\
0.0000 -44.2709
0.0500 -44.2709
0.0750 -44.2701
0.0750 -44.2702
0.1000 -44.2708
0.1250 -44.2707
0.1500 -44.2706
0.0750 -44.2703
0.0000 -20.8317
0.0250 -20.8322
0.0500 -20.8338
0.0750 -20.8364
0.0750 -20.8365
0.1000 -20.8400
0.1000 -20.8401
0.1250 -20.8445
'''
file = StringIO(data)
first, second = 'A', 'B'
df = pd.read_fwf(file, names=[first, second], skip_blank_lines=False)
df['block'] = df[first].isna().cumsum()
df = df.dropna()
df['inner_id'] = df.groupby(['block', first]).cumcount()
result = df.pivot(columns='block', index=[first,'inner_id'], values=second)
Вывод по тестовым данным:
>>> print(result)
block 0 1
A inner_id
0.000 0 -44.2709 -20.8317
0.025 0 NaN -20.8322
0.050 0 -44.2709 -20.8338
0.075 0 -44.2701 -20.8364
1 -44.2702 -20.8365
2 -44.2703 NaN
0.100 0 -44.2708 -20.8400
1 NaN -20.8401
0.125 0 -44.2707 -20.8445
0.150 0 -44.2706 NaN
По данным исходного поста, у нас в обоих блоках продублированы записи с 3.2802 в первом столбце. При запуске приведенного выше кода для этих данных результат также имеет 2 записи с индексом 3,2802:
>>> print(result.loc[3.2802])
block 0 1
inner_id
0 -44.2693 -20.9179
1 -44.2677 -20.9106
большое спасибо! Я сам нашел решение, используя некоторые предположения относительно моих конкретных данных. но затем вы показали решение numpy, используя np.diff, и оно устранило эти предположения. в основном зная number_of_bands, я могу просто разделить len(bands) на это число и получить размер фрагментов, которые мне нужны для разделения второго столбца.
Поскольку @Vitalizzare уже направил меня на правильный путь, я пишу здесь то, что в итоге использовал:
def bands_dataload(filename_glob_pattern,fermi_level):
files=glob.glob(filename_glob_pattern)
bands = np.loadtxt(files[0])
number_of_bands = 1 + np.sum(np.diff(bands[:,0]) < 0) # or 1 + sum(bands[1:, 0] < bands[:-1, 0])
# band,no_bands=np.unique(bands[:,0],return_counts=True)
# no_bands=no_bands[0]
# no_kpoints=len(bands)/no_bands
no_kpoints=len(bands)/number_of_bands
kp_bands=np.take(bands[:,0],range(0,int(no_kpoints)))
en_bands=np.split(bands[:,1]-fermi_level,len(bands)/no_kpoints)
return [kp_bands,en_bands]
Закомментированные строки были моим решением. Я использовал тот факт, что эти файлы данных весьма специфичны: блоки в первом столбце всегда одинаковы и в основном увеличиваются (за исключением нескольких значений, которые иногда равны). Таким образом, используя np.unique с return_counts=True, я мог бы получить список наиболее уникальных элементов, а затем просто выбрать первый из них, который будет no_bands, который затем использовать для определения длины каждого фрагмента, который я могу использовать в np.split.
Решение с np.diff устраняет эти предположения и сразу дает значение number_of_bands.
Благодаря этому я многому научился, в основном RTFM! Спасибо!
P.S.: Я попытаюсь осмыслить работу с pandas, но когда я попробовал, у меня возникли проблемы с индексным столбцом и строкой. Numpy мне показалось более понятным.
вы можете попробовать просмотреть фрейм данных pandas как этот вопрос