How can I only show differences to first value as GNUPlot dataline? - gnuplot

I'm Using GNUPlot to show a graph for some measuring points. ( ~ 18.500 points)
The source looks like:
Date/time | Name | Value
21.07.2022 17:00:16 | M.01 - | 4045
21.07.2022 17:00:25 | M.02 - | 1789
21.07.2022 17:00:35 | M.03 - | 3245
21.07.2022 17:04:15 | M.01 - | 4043
21.07.2022 17:05:01 | M.02 - | 1793
21.07.2022 17:05:14 | M.03 - | 3246
21.07.2022 17:09:45 | M.01 - | 4042
21.07.2022 17:10:05 | M.02 - | 1793
21.07.2022 17:10:29 | M.03 - | 3247
I'm using the folowing script:
set datafile separator ';'
set xdata time
set xlabel "Zeit"
set ylabel "Abweichung"
set timefmt "%d-%m-%Y %H:%M:%S"
plot 'D:\_Local\gnuplot\source.csv' using 1:(stringcolumn(2) eq "M.01 -"? $3:1/0) title "M.01" lc rgb "blue" , "" using 1:(stringcolumn(2) eq "M.02 -"? $3:1/0) title "M.02" lc rgb "red", "" using 1:(stringcolumn(2) eq "M.03 -"? $3:1/0) title "M.03" lc rgb "green"
This works really fine and does the job really fine.
Now I want hot have a graph, which shows me the difference from the first given value
M.01 - | -2 | -1
M.02 - | 4 | 0
M.03 - | 1 | 1
I wasn't able to find some useful examples during my google searches, so I hope someone here is able to help me for this data correlation.

Assumption is that M.01, M.02 and M.03 are strictly repeating in this order. Then you can use every, check help every. If not, it will get a bit more complicated but feasible as well.
if you keep the standard column separator whitespace your values are in the 7th column.
when the first row number index (pseudocolumn 0, check help pseudocolumns) is equal to 0 then memorize this value in y0 and subtract it from the current and future values. every 3::1 means every 3rd value starting from index 1 (i.e. the 2nd row).
do this in a loop from 1 to 3
read your color from a string with hexadecimal numbers 0xrrggbb, i.e. 0xff000=red, 0x00ff00=green and 0x0000ff=blue. Check help colorspec.
Script:
### normalize several values to first values
reset session
$Data <<EOD
Date/time | Name | Value
21.07.2022 17:00:16 | M.01 - | 4045
21.07.2022 17:00:25 | M.02 - | 1789
21.07.2022 17:00:35 | M.03 - | 3245
21.07.2022 17:04:15 | M.01 - | 4043
21.07.2022 17:05:01 | M.02 - | 1793
21.07.2022 17:05:14 | M.03 - | 3246
21.07.2022 17:09:45 | M.01 - | 4042
21.07.2022 17:10:05 | M.02 - | 1793
21.07.2022 17:10:29 | M.03 - | 3247
EOD
myColor(i) = int(word("0x0000ff 0xff0000 0x00ff00",i))
myTimeFmt = "%d.%m.%Y %H:%M:%S"
set format x "%H:%M" timedate
set offsets 1,1,1,1
set grid x,y
set key top left
plot for [i=1:3] $Data u (timecolumn(1,myTimeFmt)):($0==0?y0=$7:0,$7-y0) every 3::i \
w lp pt 7 lc rgb myColor(i) title sprintf("M.%02d",i)
### end of script
Addition:
If you can't be sure that the sequence 1,2,3 is strictly followed, you can slightly modify the script. With this, it should work in any case and any order. Note the the filter function myFilter() and the additional line set datafile missing NaN.
Script: (note the irregular order of M.01, M.02 and M.03.)
### normalize several values to first values
reset session
$Data <<EOD
Date/time | Name | Value
21.07.2022 17:00:25 | M.02 - | 1789
21.07.2022 17:00:35 | M.03 - | 3245
21.07.2022 17:00:16 | M.01 - | 4045
21.07.2022 17:04:15 | M.01 - | 4043
21.07.2022 17:05:14 | M.03 - | 3246
21.07.2022 17:05:01 | M.02 - | 1793
21.07.2022 17:10:29 | M.03 - | 3247
21.07.2022 17:10:05 | M.02 - | 1793
21.07.2022 17:09:45 | M.01 - | 4042
EOD
myColor(i) = int(word("0x0000ff 0xff0000 0x00ff00",i))
myTimeFmt = "%d.%m.%Y %H:%M:%S"
set format x "%H:%M" timedate
set offsets 1,1,1,1
set grid x,y
set key top left
set datafile missing NaN
myFilter(col,v) = strcol(col) eq sprintf("M.%02d",v) ? (c=c+1, c==1?y0=$7:0, $7-y0): NaN
plot for [i=1:3] c=0 $Data u (timecolumn(1,myTimeFmt)):(myFilter(4,i)) \
w lp pt 7 lc rgb myColor(i) title sprintf("M.%02d",i)
### end of script
Result: (identical for both scripts)

Related

Creating continous subplots with multiple x axes

I have the following dataframe:
------------------------------------------------------------
| Month | low_temp | high_temp | Wages | Extreme |
------------------------------------------------------------
| Jan | 0 | 3 | -0.42 | 1000 |
------------------------------------------------------------
| Jan | 1 | 3 | 0.56 | 3000 |
------------------------------------------------------------
| Feb | -1 | 2 | -0.61 | 2000 |
------------------------------------------------------------
| Feb | 0 | 1 | 0.36 | 3500 |
-------------------------------------------------------------
| Mar | 1.5 | 4 | -0.25 | 3000 |
-------------------------------------------------------------
| Mar | 2 | 5 | 0.75 | 4000 |
-------------------------------------------------------------
| Apr | 3 | 5 | -0.55 | 3000 |
------------------------------------------------------------
| Apr | 3.25 | 4 | 0.24 | 6000 |
-------------------------------------------------------------
What I'm trying to do is create one continuous plot with two x axes.
So, it would have the follow features:
one y-axis for low_temp and high_temp.
two lines: low_temp, high_temp
x_axis1 (the important one): Extreme
x_axis2 (below or above the x_axis1, lines up with it but isn't used to plot anything): Wages
And then, for each Month, create this chart and then string it together horizontally
so it ends up:
y --------------------------
| | | | |
| jan | feb | mar | apr |
-------------------------
x axis 1
x axis 2
This is my code attempt but it causes the x-axis to not be in line at all!
for index, month in enumerate(Months):
a = df[df['Month']==month].sort_values(by='Extreme')
x = a['Extreme']
y_1 = a['low_temp']
y_2 = a['high_temp']
plt.subplot(1,4,index+1)
plt.plot(x,y_1,'bo-')
plt.plot(x,y_2, 'ro-')
plt.xticks(a.Wages)
plt.title(month)
plt.show()
The charts also appear in a vertical line so they aren't horizontally contiguous.
Any help is very much appreciated! thanks!

excel find nearest cell that meets a criteria

I'm trying to write a formula to find the nearest cell that meets a certain criteria in short i have a table of data for an antenna that denotes its performance at various angles around it, and I want to calculate the 3dB beamwidth.
the table is in the form:
+-------------------------------------+
| Angle | Freq 1 | Freq 2 | Freq 3 |
+----------+--------+--------+--------+
| 0 | -2 | -4 | -6 |
| 10 | -2.5 | 4 | -7 |
| 20 | -2 | 5 | 0 |
| 30 | 1 | 6 | 2 |
| 40 | 4 | 7 | 2 |
| 50 | 5 | 6 | 3 |
| 60 | 4 | 6 | 2 |
| 70 | 2 | 5 | 2 |
| 80 | 0 | 4 | 2 |
| 90 | -2.5 | 2 | 1 |
| 100 | -2 | -4 | 0 |
| ... | ... | ... | ... |
| 350 | -2 | -4 | -6 |
| 360 | -2 | -4 | -6 |
+----------+--------+--------+--------+
| Max | 5 | 7 | 3 |
| Ang. Max | 50 | 40 | 50 |
+----------+--------+--------+--------+
so i currently use max to get the highest value in the table, and INDEX([angle range],MATCH([max cell],[freq column range],0)) to look up the corresponding angle of that maximum value.
I need a formula that will find the corresponding angle for the last cell above the maximum that is withing 3dB of the max and the angle for the last cell below it, then get the difference between those angles to get the beamwidth in degrees, so for Freq 1:
above: 4 - angle 40
below: 2 - angle 70
beamwidth: 70 - 40 = 30
freq 2:
above: 4 - angle 10
below: 4 - angle 80
beamwidth: 80 - 10 = 70
freq 3:
above: 0 - angle 20
below: 0 - angle 100
beamwidth: 100 - 20 = 80
ideally it would work linearly interpolate between the last cell that is withing 3dB and the first outside to guess the angle when between these steps... but that might be a bridge too far.
I want to avoid doing this with a macro because I want it to be calculated automatically any time the source data changes, and i need to be able to send this around to machines that are not allowed to run macros.
I thought about using LOOKUP of VLOOKUP but these need to match an exact value unless an option is set, but when i tried with that option set I got no matches at all for the value of (max - 3)
not really sure what else to try.
For Excel 2010 and above:
To find the Above:
=INDEX($A:$A,AGGREGATE(15,6,ROW(B$2:B$12)/(B$2:B$12>=MAX(B$2:B$12)-3),1))
To find the Below, change the 15 to 14:
=INDEX($A:$A,AGGREGATE(14,6,ROW(B$2:B$12)/(B$2:B$12>=MAX(B$2:B$12)-3),1))
Then drag/copy across to reference the next columns.
For 2007 and below you will need to use the following array formulas:
For Above:
=INDEX($A:$A,SMALL(IF(B$2:B$12>=MAX(B$2:B$12)-3,ROW(B$2:B$12)),1))
For Below:
=INDEX($A:$A,LARGE(IF(B$2:B$12>=MAX(B$2:B$12)-3,ROW(B$2:B$12)),1))
Being Array formulas they need to be confirmed with Ctrl-Shift-Enter instead of Enter when exiting edit mode. If done correctly then Excel will put {} around the formula.

Looking to create weighted average of partitioned columns in Excel

Horrible title, but I couldn't find a way to describe what I'm trying to do concisely. This question was posed to me by a friend, and I'm usually competent in Excel, but in this case I am totally stumped.
Suppose I have the following data:
| A | B | C | D | E | F | G | H |
---------------------------------------------------------------------
1 | 0.50 | 0.50 | 1 | | | 0.30 | 0.30 | |
2 | 0.25 | 0.75 | 2 | | | 0.40 | 0.70 | |
3 | 1.00 | 1.75 | 8 | | | 0.30 | 1.00 | |
4 | 0.75 | 2.50 | 2 | | | 0.50 | 1.50 | |
5 | 1.25 | 3.75 | 3 | | | 1.75 | 3.25 | |
6 | 0.50 | 4.25 | 1 | | | 0.25 | 3.50 | |
7 | 1.00 | 5.25 | 0 | | | 0.50 | 4.00 | |
8 | 0.25 | 5.50 | 2 | | | 0.30 | 4.30 | |
9 | 0.25 | 5.75 | 9 | | | 0.25 | 4.55 | |
10 | 0.75 | 6.50 | 4 | | | 0.70 | 5.25 | |
11 | | | | | | 1.00 | 6.25 | |
12 | | | | | | 0.25 | 0.25 | |
Column A represents the distance traveled while the measurement in column C was collected. Column B represents the total distance traveled so far. So C1 represents some value produced during the process from distance 0 to 0.5. B2 represents the value from distance 0.5 to 0.75, and B3 represents the value from 0.75 to 1.75, etc...
Column F represents a PLANNED second iteration of the same process, but with different measurement intervals. What I need is a way to PREDICT column H, based on a WEIGHTED AVERAGE of values from column C, based on where the intervals in column F intersect with the intervals in column A. For example, since F2 represents the measurement taken from distance 0.30 to 0.70 (an interval of 0.4, split 50/50 across the measurements in C1 and C2), H2 would be equal to: C1*0.5 + C2*0.5: 1.5.
Another example: H3 represents the expected measurement from an interval between 0.7 and 1.0, which is split between C2 (from 0.7 to 0.75 = 0.05) and C3 (from 0.75 to 1.0 = 0.25). So H3 = 16.6%*C2 + 83.3%*C3 = 0.332+6.664 = 6.996.
I'm looking for a way to do this in an Excel spreadsheet without using VBA or breaking it down into something like a Python script to process externally, but so far I'm not finding any way to do it.
Any ideas for accomplishing this entirely within Excel without any special add-ins/scripts installed ?
It's not pretty, but I think the following should work for all except H1 (which would need an added zero row):
=(MAX(0,INDEX(B:B,MATCH(G2,B:B,1))-G1)*INDEX(C:C,MATCH(G2,B:B,1)) +
(G2-INDEX(B:B,MATCH(G2,B:B,1)))*INDEX(C:C,MATCH(G2,B:B,1)+1)) /
MAX(G2-G1,G2-INDEX(B:B,MATCH(G2,B:B,1)))
It matches the values in B and C and weights them accordingly.

Converting a list of values to between -1 and 1

I have a questionnaire with answers in a number of different formats. I want the range to be between -1 and 1. However, not all ranges include negative numbers.
I need to create an excel formula to convert the value to the following dependent upon the range.
+---+--------+
| A |To this |
+---+--------+
|-3 | -1 |
|-2 | -0.66 |
|-1 | -0.33 |
| 0 | 0 |
| 1 | 1 |
+---+--------+
Or
+---+--------+
| A |To this |
+---+--------+
| 0 | 0 |
| 1 | 0.25 |
| 2 | 0.5 |
| 3 | 0.75 |
| 4 | 1 |
+---+--------+
Or
+---+--------+
| A |To this |
+---+--------+
| 1 | 0.2 |
| 2 | 0.4 |
| 3 | 0.6 |
| 4 | 0.8 |
| 5 | 1 |
+---+--------+
Or
+---+--------+
| A |To this |
+---+--------+
|-2 | -1 |
|-1 | -0.5 |
| 0 | 0 |
| 1 | 0.5 |
| 2 | 1 |
+---+--------+
etc.
This formula should do the trick:
=IFERROR(IF(A1<=0,-1*A1/(MIN(A:A)+MIN(0,MAX(A:A))),A1/(MAX(A:A))),0)
This produces this example output when autofilled down:
-3 -1
-2 -0.666666667
-1 -0.333333333
0 0
1 0.2
2 0.4
3 0.6
4 0.8
5 1
Note: this includes 0 for both sets of -1,0 and 0,1
If the range of input numbers is finite, even with negative numbers, you can use the general range mapping formula as below.
If the range of input numbers is [X1:X2] and the range of output numbers is [Y1:Y2] (in your case [-1:+1]) then number x is mapped to number y in the output range with the following formula:
y = (x - X1) * (Y2 - Y1) / (X2 - X1) + Y1
when X2-X1 != 0

Redundant space at right side of Y axis

I'm trying to make plot with GNU Plot for some data of experiments, but I cannot figure how to remove the redundant padding/space between Y axis and the first sample (left side of the first sample):
Code:
set terminal pngcairo size 800,800 enhanced font 'WenQuanYiZenHei,12'
set title "All Elements"
set xlabel "Sample"
set ylabel "Element Amounts"
set ytics nomirror
set style fill transparent solid 0.5 noborder
plot data u 2:xticlabels(1) w histograms title "K",\
data u 3:xticlabels(1) w histograms title "Ca",\
data u 4:xticlabels(1) w histograms title "Mg",\
data u 5:xticlabels(1) w histograms title "Fe",\
data u 6:xticlabels(1) w histograms title "Mn",\
data u 7:xticlabels(1) w histograms title "Zn"
Data:
| Sample | K | Ca | Mg | Fe | Mn | Zn |
|--------+-------+-------+------+--------+-------+------|
| -Ca 1U | 58800 | 4800 | 2700 | 222.5 | 28.0 | 30.0 |
| -Ca 2U | 59000 | 5475 | 3200 | 105.5 | 29.5 | 35.0 |
| -Mg 1U | 57600 | 12275 | 2900 | 92.0 | 45.5 | 37.0 |
| -Mg 2U | 57200 | 13850 | 3200 | 266.0 | 59.5 | 39.0 |
|--------+-------+-------+------+--------+-------+------|
| -Ca 1D | 19700 | 1100 | 3400 | 1708.0 | 79.0 | 48.5 |
| -Ca 2D | 20900 | 1025 | 3300 | 1812.0 | 102.5 | 54.5 |
| -Mg 1D | 23200 | 3175 | 3200 | 312.0 | 49.5 | 61.0 |
| -Mg 2D | 21800 | 4325 | 2300 | 2136.0 | 86.5 | 55.5 |
That's because of the header line in the data. You can skip it with
plot data every ::1 u 2: ...
Or just remove it before feeding the data to gnuplot.

Resources