Построение большого текстового файла, содержащего матрицу, с помощью gnuplot / matplotlib

В целях отладки моя программа записывает матрицы на основе броненосцев в формате raw-ascii в текстовые файлы, т.е. комплексные числа записываются как (1, 1). Более того, полученные матрицы дают размер файла ›3 ГБ.

Я хотел бы построить эти матрицы (представляющие поля), чтобы я мог смотреть на разные точки в поле для отладки. Как лучше всего это сделать?

При прямом построении моего файла с помощью gnuplot, используя

plot "matrix_file.txt" matrix with image

Я получаю ответ

warning: matrix contains missing or undefined values
Warning: empty cb range [0:0], adjusting to [-1:1]

Я также мог бы использовать Matplotlib, перебирать каждую строку в файле и преобразовывать значения в соответствующие значения python, но я предполагаю, что чтение полного файла будет довольно трудоемким.

Таким образом, есть ли другие разумные быстрые варианты построения моей матрицы или есть способ указать gnuplot, как правильно обрабатывать мои комплексные числа?

Часть первой строки выглядит как

(0.0000000000000000e+00,0.0000000000000000e+00) (8.6305562282169946e-07,6.0526580514090297e-07) (1.2822974500623326e-05,1.1477679031930141e-05) (5.8656372718492336e-05,6.6626342814082442e-05) (1.6183121649896915e-04,2.3519364967920469e-04) (3.2919257507746272e-04,6.2745022681547850e-04) (5.3056616247733281e-04,1.3949688132772061e-03) (6.7714688179733437e-04,2.7240206117506108e-03) (6.0083005524875425e-04,4.8217990806492588e-03) (3.6759450038482363e-05,7.8957232784174231e-03) (-1.3887302495780910e-03,1.2126758313515496e-02) (-4.1629396217170980e-03,1.7638346107957101e-02) (-8.8831593853181175e-03,2.4463072133103888e-02) (-1.6244140097742808e-02,3.2509486873735290e-02) (-2.7017231109227786e-02,4.1531431496659221e-02) (-4.2022691198292300e-02,5.1101686500864850e-02) (-6.2097364532786636e-02,6.0590740956970250e-02) (-8.8060067117896060e-02,6.9150058884242055e-02) (-1.2067637255414780e-01,7.5697648270160053e-02) (-1.6062285417043359e-01,7.8902435158400494e-02) (-2.0844826713055306e-01,7.7163461035715558e-02) (-2.6452596415873003e-01,6.8580842184681204e-02) (-3.2898869195273894e-01,5.0918234150147214e-02) (-4.0163477687695504e-01,2.1561405580661022e-02) (-4.8179470918233597e-01,-2.2515842273449008e-02) (-5.6815035401912617e-01,-8.4759639628930100e-02) (-6.5850621484774385e-01,-1.6899215347429869e-01) (-7.4952345707877654e-01,-2.7928561041518252e-01) (-8.3644196044174313e-01,-4.1972419090890900e-01) (-9.1283160402230334e-01,-5.9403043419268908e-01) (-9.7042844114238713e-01,-8.0504703287094281e-01) (-9.9912107865273936e-01,-1.0540865412492695e+00) (-9.8715384989307420e-01,-1.3401890190155983e+00) (-9.2160320921981831e-01,-1.6593576679224276e+00) (-7.8916051033438095e-01,-2.0038702251062159e+00) (-5.7721850912406181e-01,-2.3617835609973805e+00) (-2.7521347260072193e-01,-2.7167550691449942e+00)

В идеале я хотел бы иметь возможность выбирать, отображать ли я только реальную часть, мнимую часть или значение abs().


person arc_lupus    schedule 04.03.2021    source источник
comment
Вы говорите, что файл большой ... но, может быть, вы сможете показать очень маленький отрывок? Подойдет минимальный пример с данными. Думаю, проблема в комплексных числах. Прежде всего, в gnuplot записываются комплексные числа, например {1,1} но я думаю, их нельзя нарисовать напрямую. Как вы хотите, чтобы они были построены? Реальная часть или мнимая часть?   -  person theozh    schedule 04.03.2021
comment
@theozh: я добавил пример и то, что я хотел бы построить   -  person arc_lupus    schedule 04.03.2021
comment
Спасибо. Собственно, что вы хотите построить? Номер столбца как x, номер строки как y и абсолютное значение вашего комплексного числа (=sqrt(Re^2+Im^2) как цвет или что-то еще?   -  person theozh    schedule 04.03.2021
comment
Либо реальная часть, либо мнимая часть, либо абсолютное значение моего комплексного числа как цвета, да   -  person arc_lupus    schedule 04.03.2021


Ответы (2)


Вот версия только для gnuplot. На самом деле, я не видел (пока) примера gnuplot о том, как строить комплексные числа из файла данных. Здесь идея состоит в том, чтобы разделить данные на столбцы с символами (, , и ) с помощью:

set datafile separator '(,)'

Затем вы можете указать свою i-ю действительную и мнимую части в столбце через column(3*i-1) и column(3*i) соответственно.

Вы создаете новый набор данных, многократно отображая данные в двойном цикле, что подходит для небольших данных. Однако я предполагаю, что это решение может стать довольно медленным для больших наборов данных, особенно если вы строите график из файла. Я предполагаю, что если у вас есть данные один раз в блоке данных (а не в файле), это может быть быстрее. Проверьте gnuplot: загрузить файл данных 1: 1 в блок данных. В общем, может быть, более эффективно использовать другой инструмент, например Python, awk и т. Д. Для подготовки данных.

Подумал только: если у вас ок. 3e9 байтов данных и (согласно вашему примеру) прибл. 48-50 байтов на точку данных, и если вы хотите построить квадратный график, то количество пикселей на стороне будет sqrt(3e9/50)=7746 пикселей. Сомневаюсь, что у вас есть дисплей, который сразу это отображает.

Изменить:

В приведенной ниже модифицированной версии теперь используется set print для блокировки данных, и она намного быстрее, чем исходная версия (с использованием двойного цикла plot ... every ...). Улучшение скорости я уже вижу на моем небольшом примере данных. Удачи с вашим огромным набором данных ;-). Для справки и сравнения старая версия снова указана здесь:

# create a new datablock with row,col,Real,Imag,Abs
# using plot ...with table     (pretty slow and inefficient)
set table $Data2
    set datafile separator '(,)'          # now, split your data at these characters
    myReal(i) = column(3*i-1)
    myImag(i) = column(3*i)
    myAbs(i)  = sqrt(myReal(i)**2 + myImag(i)**2)
    plot for [row=0:rowMax-1] for [col=1:colMax] $Data u (row):(col):(myReal(col)):(myImag(col)):(myAbs(col)) every ::row::row w table
    set datafile separator whitespace     # set separator back to whitespace
unset table

Код: (изменен с использованием set print)

### plotting complex numbers
reset session

$Data <<EOD
(0.1,0.1)   (0.2,1.2)   (0.3,2.3)   (0.4,3.4)   (0.5,4.5)
(1.1,0.1)   (1.2,1.2)   (1.3,2.3)   (1.4,3.4)   (1.5,4.5)
(2.1,0.1)   (2.2,1.2)   (2.3,2.3)   (2.4,3.4)   (2.5,4.5)
(3.1,0.1)   (3.2,1.2)   (3.3,2.3)   (3.4,3.4)   (3.5,4.5)
(4.1,0.1)   (4.2,1.2)   (4.3,2.3)   (4.4,3.4)   (4.5,4.5)
(5.1,0.1)   (5.2,1.2)   (5.3,2.3)   (5.4,3.4)   (5.5,4.5)
(6.1,0.1)   (6.2,1.2)   (6.3,2.3)   (6.4,3.4)   (6.5,4.5)
(7.1,0.1)   (7.2,1.2)   (7.3,2.3)   (7.4,3.4)   (7.5,4.5)
EOD

stats $Data u 0 nooutput   # get number of columns and rows, separator is whitespace
colMax = STATS_columns
rowMax = STATS_records

# create a new datablock with row,col,Real,Imag,Abs
# using print to datablock
set print $Data2
    myCmplx(row,col) = word($Data[row+1],col)
    myReal(row,col) = (s=myCmplx(row,col),s[2:strstrt(s,',')-1])
    myImag(row,col) = (s=myCmplx(row,col),s[strstrt(s,',')+1:strlen(s)-1])
    myAbs(row,col)  = sqrt(myReal(row,col)**2 + myImag(row,col)**2)
    do for [row=0:rowMax-1] {
        do for [col=1:colMax] {
            print sprintf("%d %d %s %s %g",row-1,col,myReal(row,col),myImag(row,col),myAbs(row,col))
        }
    }
set print

set key box opaque

set multiplot layout 2,2
    plot $Data2 u 1:2:3 w image ti "Real part"
    plot $Data2 u 1:2:4 w image ti "Imaginary part"
    set origin 0.25,0
    plot $Data2 u 1:2:5 w image ti "Absolute value"
unset multiplot
### end of code

Результат:

введите описание изображения здесь

person theozh    schedule 06.03.2021
comment
Хорошее решение, протестирую как можно скорее! - person arc_lupus; 06.03.2021
comment
@arc_lupus Некоторое беспокойство по поводу этого решения вместе с большими данными. Фактически, блок данных $Data2 дополнительно увеличен, потому что он содержит _2 _, _ 3 _, _ 4 _, _ 5_ и Abs. Если ваша система перегружена вашим набором данных, я бы предложил создать матрицу Real-only, матрицу Imag-only и матрицу Abs в отдельных скриптах gnuplot для построения графиков. - person theozh; 07.03.2021
comment
@arc_lupus, поскольку вы приняли ответ, я полагаю, он работал с вашим файлом данных размером 3 ГБ ...? Рад слышать, что gnuplot может с этим справиться. - person theozh; 08.03.2021

Может быть, это не то, о чем вы просили, но я думаю, что удобно строить график непосредственно из вашего кода, и легко изменить то, что вы хотите показать abs(x),real(x),... Вот простой фрагмент для построения матрицы Армадилло как изображения в gnuplot (Linux)

#include <armadillo>
using namespace std;
using namespace arma;

void plot_image(mat& x, FILE* cmd_pipe)
{    
  fputs("set nokey;set yrange [*:*] reverse\n", cmd_pipe);
  fputs("plot '-' matrix with image\n", cmd_pipe);
  for(uword r=0; r<x.n_rows; r++){
    for(uword c=0; c<x.n_cols; c++){
      string str=to_string(x(r,c))+" ";
      fputs(str.c_str(), cmd_pipe);
    }
    fputs("\n", cmd_pipe);
  }
  fputs("e\n", cmd_pipe);
}

int main()
{
  FILE* gnuplot_pipe = popen("gnuplot -persist","w");
    
  mat x={{1,2,3,4,5},
         {2,2,3,4,5},
         {3,3,3,4,5},
         {4,4,4,4,5},
         {5,5,9,9,9}};
       
  plot_image(x,gnuplot_pipe);        
  return 0 ;    
}

Результат:  введите описание изображения здесь

person Claes Rolen    schedule 04.03.2021