Generate area plot for time series in MATLAB - matlab

I'm trying to generate this figure shown below:
(source: noaa.gov)
The data that I am using to try and generate this figure can be found here data.
This is a subset of the data that I extracted:
d = [1950 1 0.56
1950 2 0.01
1950 3 -0.78
1950 4 0.65
1950 5 -0.50
1950 6 0.25
1950 7 -1.23
1950 8 -0.19
1950 9 0.39
1950 10 1.43
1950 11 -1.46
1950 12 -1.03
1951 1 -0.42
1951 2 0.35
1951 3 -1.47
1951 4 -0.38
1951 5 -0.50
1951 6 -1.35
1951 7 1.39
1951 8 -0.41
1951 9 -1.18
1951 10 2.54
1951 11 -0.54
1951 12 1.13
1952 1 0.57
1952 2 -1.38
1952 3 -1.97
1952 4 0.95
1952 5 -0.99
1952 6 -0.10
1952 7 -0.06
1952 8 -0.49
1952 9 -0.38
1952 10 -0.28
1952 11 -1.32
1952 12 -0.49
1953 1 -0.12
1953 2 -1.00
1953 3 -0.45
1953 4 -1.96
1953 5 -0.56
1953 6 1.41
1953 7 0.43
1953 8 -1.04
1953 9 -0.19
1953 10 1.95
1953 11 0.96
1953 12 -0.52
1954 1 -0.08
1954 2 0.40
1954 3 -1.27
1954 4 1.31
1954 5 -0.03
1954 6 0.06
1954 7 -0.57
1954 8 -2.57
1954 9 -0.28
1954 10 1.16
1954 11 0.29
1954 12 0.55
1955 1 -2.65
1955 2 -1.71
1955 3 -0.96
1955 4 -0.60
1955 5 -0.26
1955 6 -0.80
1955 7 1.78
1955 8 1.25
1955 9 0.46
1955 10 -1.09
1955 11 -1.49
1955 12 0.07
1956 1 -0.76
1956 2 -1.71
1956 3 -0.46
1956 4 -1.30
1956 5 2.10
1956 6 0.41
1956 7 -0.72
1956 8 -1.89
1956 9 0.38
1956 10 1.47
1956 11 0.40
1956 12 0.00
1957 1 0.71
1957 2 -0.32
1957 3 -1.73
1957 4 0.39
1957 5 -0.68
1957 6 -0.42
1957 7 -1.16
1957 8 -0.83
1957 9 -1.47
1957 10 1.95
1957 11 0.63
1957 12 0.02
1958 1 -1.14
1958 2 -1.64
1958 3 -2.46
1958 4 0.26
1958 5 -0.17
1958 6 -1.08
1958 7 -1.69
1958 8 -2.13
1958 9 0.08
1958 10 0.68
1958 11 1.59
1958 12 -0.74
1959 1 -1.52
1959 2 0.33
1959 3 -0.56
1959 4 0.25
1959 5 0.41
1959 6 0.71
1959 7 0.77
1959 8 -0.05
1959 9 1.00
1959 10 1.48
1959 11 0.30
1959 12 0.32
1960 1 -2.01
1960 2 -2.59
1960 3 -0.93
1960 4 1.33
1960 5 0.47
1960 6 0.10
1960 7 0.38
1960 8 -1.93
1960 9 0.53
1960 10 -1.37
1960 11 -0.67
1960 12 -0.03
1961 1 -0.03
1961 2 0.07
1961 3 0.17
1961 4 -1.83
1961 5 -0.28
1961 6 1.17
1961 7 -0.36
1961 8 1.03
1961 9 1.36
1961 10 1.07
1961 11 -0.79
1961 12 -1.46
1962 1 0.20
1962 2 0.18
1962 3 -2.99
1962 4 0.93
1962 5 -0.04
1962 6 0.47
1962 7 -2.43
1962 8 0.05
1962 9 -0.21
1962 10 0.96
1962 11 -0.38
1962 12 -1.31
1963 1 -2.98
1963 2 -1.53
1963 3 -0.85
1963 4 -1.61
1963 5 2.05
1963 6 -0.13
1963 7 -0.74
1963 8 -0.95
1963 9 1.89
1963 10 1.53
1963 11 -1.47
1963 12 -1.87
1964 1 -1.62
1964 2 -2.06
1964 3 -1.66
1964 4 0.25
1964 5 0.53
1964 6 1.61
1964 7 1.92
1964 8 -2.40
1964 9 0.34
1964 10 1.32
1964 11 -0.14
1964 12 -0.23
1965 1 -0.65
1965 2 -2.20
1965 3 -1.99
1965 4 0.64
1965 5 -0.52
1965 6 0.60
1965 7 0.35
1965 8 0.45
1965 9 0.51
1965 10 0.92
1965 11 -1.88
1965 12 1.18
1966 1 -2.54
1966 2 -2.02
1966 3 0.18
1966 4 -0.96
1966 5 0.25
1966 6 1.37
1966 7 0.35
1966 8 -2.39
1966 9 -0.29
1966 10 -0.23
1966 11 -0.18
1966 12 0.58];
I've tried the code below to generate the above plot:
time = datenum(d(:,1),d(:,2),repmat(15,size(d,1),1));
nao = d(:,3);
figure(1);
ax1 = subplot(211);
area(time(nao < 0),nao(nao < 0),'FaceColor',[86 180 233]/255,'EdgeColor','none');
hold on;
area(time(nao > 0),nao(nao > 0),'FaceColor','r','EdgeColor','none');
datetick('x','yyyy','keeplimits');
ax2 = subplot(212);
bar(time(nao < 0),nao(nao < 0),'FaceColor',[86 180 233]/255,'EdgeColor','none');
hold on;
bar(time(nao > 0),nao(nao > 0),'FaceColor','r','EdgeColor','none');
datetick('x','yyyy','keeplimits');
linkaxes([ax1 ax2], 'x');
However, the output is not the same as that shown in the original figure, granted that they have a three month moving average. Ideally, I would have something that looked half like each of the plots I generated. That is, an area plot but with only a red or blue color at any given time. The area function seems to show these to happen at the same time, which is not correct. Any advice on how to generate the attached figure would be appreciated.

Using areashade from MATLAB FileExchange, the following should produce what you need:
figure(2);
areashade(time, nao, 0, [86 180 233]/255, 'h')
areashade(time, nao, 0,'r','l')
datetick('x','yyyy','keeplimits');

Related

Assign weight and rate to products based on size of variety & weight

I have table1
line
products
product variety
weight
rate
1
a
49
2
b
27
3
c
26
4
d
28
5
e
7
6
f
6
7
g
7
8
h
13
9
i
12
10
j
13
11
k
13
12
l
3
13
m
6
14
n
13
15
o
5
16
p
5
17
q
10
18
r
707
19
s
26
20
t
10
21
u
10
22
v
10
23
w
5
24
x
2
25
y
2
26
z
2
Want to assign weight to each products based on the size of product variety and rate will be calculated based on the weight like, the 1st largest weight = 1, the 2nd largest weight = 2 and so on. The total sum of weights should be 100
The expected result looks like below
line
products
product variety
weight
rate
1
a
49
                 6.38
2
2
b
27
                 5.40
4
3
c
26
                 5.34
5
4
d
28
                 5.46
3
5
e
7
                 3.19
9
6
f
6
                 2.94
10
7
g
7
                 3.19
9
8
h
13
                 4.20
6
9
i
12
                 4.07
7
10
j
13
                 4.20
6
11
k
13
                 4.20
6
12
l
3
                 1.80
12
13
m
6
                 2.94
10
14
n
13
                 4.20
6
15
o
5
                 2.64
11
16
p
5
                 2.64
11
17
q
10
                 3.77
8
18
r
707
               10.75
1
19
s
26
                 5.34
5
20
t
10
                 3.77
8
21
u
10
                 3.77
8
22
v
10
                 3.77
8
23
w
5
                 2.64
11
24
x
2
                 1.14
13
25
y
2
                 1.14
13
26
z
2
                 1.14
13
Tried in excel with the below formulas and is working perfectly, but i want something like this in postgresql to provide same above result.
D2=LOG10(C2)/SUM(LOG10($C$2:$C$27))*100
E2=SUMPRODUCT(($D2 < $D$2:$D$27)/COUNTIF($D$2:$D$27,$D$2:$D$27))+1
Calculate weights in the inner query and use the window function dense_rank():
select
line,
products,
product_variety,
weight,
dense_rank() over (order by weight desc) as rate
from (
select
line,
products,
product_variety,
round(log(product_variety::dec)/ sum* 100, 2) as weight
from table1
cross join lateral (
select sum(log(product_variety::dec))
from table1
) s
) s
order by line
Test the query in db<>fiddle.

How to pick a row from a table then add it to another table

guys, I have two matrices like below. I want to compare the second column of the first matrix with the first column of the second matrix. If they are the same I want to copy that row to another matrix. I gave an example below. Thank you so much.
Matrix 1 Matrix 2
3323 918 918 2 12 13
2522 842 918 2 9 13
3049 918 918 2 6 13
1143 40 918 2 3 1
204 1 842 2 3 8
1663 362 842 3 12 9
2079 362 842 3 9 3
770 1 40 3 9 6
2189 842 40 3 6 9
40 3 3 5
Expected Result
918 2 12 13
918 2 9 13
918 2 6 13
918 2 3 1
842 2 3 8
842 3 12 9
842 3 9 3
918 2 12 13
918 2 9 13
918 2 6 13
918 2 3 1
40 3 9 6
40 3 6 9
40 3 3 5
842 2 3 8
842 3 12 9
842 3 9 3
As your metod to generate result_Matrix (call it RM) is a bit descriptive, the method will also be a bit complicated. First you have to analyse Marix 2 (I will call it M2). You can use simple loop:
i=1;
M2_indexes=[1];
for idx=1:size(M2)
if M2(idx)~=M2(M2_indexes(i))
M2_indexes=[M2_indexes, idx];
i=i+1;
end
end
M2_indexes=[M2_indexes,(size(M2,1)+1)];
Now your program knows that the value changes at [1 5 8] and the last index in the range of matrix is [11]. It's a time to iteratively loop through M1. It's very similar to previous step:
MR=[];
for idx=1:size(M1)
for idy=1:size(M2_indexes,2)
if M2(M2_indexes(idy))==M1(idx,2)
MR=[MR; M2(M2_indexes(idy):(M2_indexes(idy+1)-1),1:4)];
end
end
end
And the result is as you described:
MR =
918 2 12 13
918 2 9 13
918 2 6 13
918 2 3 1
842 2 3 8
842 3 12 9
842 3 9 3
918 2 12 13
918 2 9 13
918 2 6 13
918 2 3 1
40 3 9 6
40 3 6 9
40 3 3 5
842 2 3 8
842 3 12 9
842 3 9 3

Extract rows of matrices with nearest days record: MATLAB

I have two matrices A and B. Both have different sizes and 1st, 2nd, 3rd, & 4th value show year, month, day and values in both matrices. I need to extract rows with same year and month however, day of +/-6 days from matrix A and related rows form matrix B. If two or more days are close in matrices A & B, I should choose the rows corresponding to highest value from both matrices.
A = 1954 1 16 2,3042
1954 12 5 2,116
1954 12 21 1,9841
1954 12 22 2,7411
1955 1 13 1,8766
1955 10 16 1,4003
1955 12 29 1,4979
1956 1 19 2,1439
1956 1 21 1,7666
1956 11 26 1,7367
1956 11 27 1,8914
1957 1 27 1,151
1957 2 2 1,1484
1957 12 29 1,1906
1957 12 30 1,3157
1958 1 10 1,6186
1958 1 20 1,1637
1958 2 6 1,1639
1958 10 16 1,1444
1959 1 3 1,7784
1959 1 24 1,1871
1959 2 20 1,2264
1959 10 25 1,2194
1960 6 29 1,2327
1960 12 4 1,7213
1960 12 5 1,373
1961 3 21 1,7149
1961 3 27 1,4404
1961 11 3 1,3934
1961 12 5 1,777
1962 2 12 2,1813
1962 2 16 3,5776
1962 2 17 1,9236
1963 9 27 1,6164
1963 10 13 1,786
1963 10 14 1,9203
1963 11 22 1,7575
1964 2 2 1,4402
1964 11 15 1,437
1964 11 17 1,7588
1964 12 4 1,6358
1965 2 13 1,874
1965 11 2 2,6468
1965 11 26 1,7163
1965 12 11 1,8283
1966 12 1 2,1165
1966 12 19 1,6672
1966 12 24 1,8173
1966 12 25 1,4923
1967 2 23 2,3002
1967 3 1 1,9614
1967 3 18 1,673
1967 11 12 1,724
1968 1 4 1,6355
1968 1 15 1,6567
1968 3 6 1,1587
1968 3 18 1,212
1969 9 29 1,5613
1969 10 1 1,5016
1969 11 20 1,9304
1969 11 29 1,9279
1970 10 3 1,9859
1970 10 28 1,4065
1970 11 4 1,4227
1970 11 9 1,7901
B = 1954 12 28 774
1954 12 29 734
1955 3 26 712
1955 3 27 648
1956 7 18 1030
1956 7 23 1090
1957 2 17 549
1957 2 28 549
1958 2 27 759
1958 2 28 798
1959 1 10 421
1959 1 24 419
1960 12 5 762
1960 12 8 829
1961 2 12 788
1961 2 13 776
1962 2 15 628
1962 4 9 628
1963 3 12 552
1963 3 13 552
1964 2 12 260
1964 2 13 253
1965 12 22 862
1965 12 23 891
1966 1 5 828
1966 12 27 802
1967 1 1 777
1967 1 2 787
1968 1 17 981
1968 1 18 932
1969 3 15 511
1969 3 16 546
1970 2 25 1030
1970 2 26 1030
The expected output is a new matrix C:
C = 1954 12 22 2,7411 1954 12 28 774
1959 1 3 1,7784 1959 1 10 421
1959 1 24 1,1871 1959 1 24 419
1960 12 4 1,7213 1960 12 8 829
1962 2 12 2,1813 1962 2 15 628
1966 12 24 1,8173 1966 12 27 802
1968 1 15 1,6567 1968 1 17 981
Any help how to code this?
I think the following should do what you want -
To deal with overlaps at year and month boundaries, it's useful to have the dates mapped to number of days since an epoch. The first function finds the earliest data in either dataset, and then formats it to be interpreted by the 'daysact' function.
function epoch_date_str = get_epoch_datestr(A,B)
Astr = int2str(A(:,1:3));
Bstr = int2str(B(:,1:3));
[epoch_Ay, epoch_Am, epoch_Ad] = earliest_date(A);
[epoch_By, epoch_Bm, epoch_Bd] = earliest_date(B);
[epoch_y, epoch_m, epoch_d] = earliest_date([epoch_Ay, epoch_Am, epoch_Ad; epoch_By, epoch_Bm, epoch_Bd]);
epoch_str = int2str([epoch_y, epoch_m, epoch_d]);
epoch_date_str = regexprep(epoch_str,'\s+','/')
end
This function then does the calculation of the number of days from the epoch to each date in the dataset, it's basically just wrangling data into a format accepted by the daysact function.
function ndays = days_since_epoch(A, epoch_date_str)
ndays = zeros(size(A,1),1);
Astr = int2str(A(:,1:3));
for i=1:size(Astr,1)
ndays(i) = daysact(epoch_date_str, regexprep(Astr(i,:),'\s+','/'));
end
end
And now we can get on with the actual calculations - I was a bit confused by the fifth column in the 'A' matrix you presented, I assume that is the score, but if not it's configured by the A_MATRIX_SCORE_COL variable. Similarly the 6 day window is configured by the WINDOW_SIZE.
ep_str = get_epoch_datestr(A,B);
ndaysA = days_since_epoch(A, ep_str);
ndaysB = days_since_epoch(B, ep_str);
C = [];
WINDOW_SIZE= 6;
A_MATRIX_SCORE_COL = 5;
for i=1:length(B)
% Find dates within the date window
overlaps = find(ndaysA >= (ndaysB(i) - window_size ) & (ndaysA <= (ndaysB(i) + window_size )));
% If there are multiple matches, choose the highest and append to C
if (length(overlaps) > 0)
[~, max_idx] = max(A(overlaps,A_MATRIX_SCORE_COL));
match_row = overlaps(max_idx);
C = [C; A(match_row,:) B(i,:)];
end
end
C = unique(C,'rows');
The output I get differs from yours:
C =
1954 12 22 2 7411 1954 12 28 774
1959 1 24 1 1871 1959 1 24 419
1960 12 4 1 7213 1960 12 5 762
1960 12 4 1 7213 1960 12 8 829
1962 2 16 3 5776 1962 2 15 628
1966 12 24 1 8173 1966 12 27 802
1968 1 15 1 6567 1968 1 17 981
1968 1 15 1 6567 1968 1 18 932
But your second row has a difference of 7 days, so I wouldn't expect it to be found. It can be included by increasing the window_size to 7.
As you can see, it's possible for a row in A to be included twice in C if it matches more than one date in B. This could be easily filtered from C if you want:
D = []
for i = 1:size(C,1)
% Find matching dates from A. Due to the way C was built, there won't be duplicates from B.
dupes = find((C(:,1) == C(i,1) & C( :,2) == C(i,2) & C( :,3) == C(i,3)))
% If there's only one match (i.e. it matches itself), then add to D
if (length(dupes) == 1)
D = [D; C(i,:)]
else
% If there are duplicates, then compare the scores from B and only add the highest score to D.
best = true;
for j=1:length(dupes)
if C(i,end) < C(dupes(j),end)
best = false;
end
end
if (best == true)
D = [D; C(i,:)]
end
end
end
The matrix 'D' is then your de-duplicated output.

extract the information from a matrix with three columns

I have a matrix with three columns
https://www.dropbox.com/s/jckdmg1p05v8lv7/y.mat?dl=0
i.e.
E1 E2 W
6 1464 0.36
6 1534 0.27
6 1585 0.27
8 1331 0.332
11 445 0.39
13 844 0.286
14 12 0.126
18 952 0.31
19 2376 0.32
20 394 0.22
20 399 0.22
20 589 0.22
21 321 0.22
21 1187 0.22
21 2509 0.22
22 1187 0.22
23 2235 0.22
24 2376 0.22
25 541 0.14
26 229 0.22
26 321 0.22
26 1187 0.22
26 2054 0.22
27 394 0.53
27 541 0.31
28 394 0.22
28 781 0.22
I used this condition
for k=1:size(y,1)
G(y(k,1),y(k,2))=true;
G(y(k,2),y(k,1))=true;
end
B=cellfun(#(x1) find(x1),num2cell(G,2),'un',0);
to extract links information like this:
1 394
2 2378
3 282
4 282
5 536
6 [1464,1534,1585]
7 2087
8 [394,399,1331]
9 1187
I need a third column contains the weight
e.i. {6,[1464,1534,1585],[0.36;0.27;0.27]}
I tried to use the above condition but I did not get the right values. Does anyone have idea how to do that ??
this is a possible soultion using accumarray:
a=[...
6 1464 0.36
6 1534 0.27
6 1585 0.27
8 1331 0.332
11 445 0.39
13 844 0.286
14 12 0.126
18 952 0.31
19 2376 0.32
20 394 0.22
20 399 0.22
20 589 0.22
21 321 0.22
21 1187 0.22
21 2509 0.22
22 1187 0.22
23 2235 0.22
24 2376 0.22
25 541 0.14
26 229 0.22
26 321 0.22
26 1187 0.22
26 2054 0.22
27 394 0.53
27 541 0.31
28 394 0.22
28 781 0.22];
% concatenate a with its copy, columns 1 and 2 swapped regarding symmetric relations
a = [a ; [fliplr(a(: , 1:2)) , a(: , 3) ]];
%create proper increasing indices for use in accumarray
[S SI] = sort(a(:,1));
S2=[0; (cumsum(diff(S)>0))];
idx = a(:,1);
idx(SI) = S2+1;
%gather elemets for each category
c1=accumarray([idx],a(:,1),[],#(x) {x(1)});
c2=accumarray([idx],a(:,2),[],#(x) {x});
c3=accumarray([idx],a(:,3),[],#(x) {x});
%concatenate columns
out=([c1 c2 c3]);
% your example
out(1,:)

Compute laplacian using gradient

I'm trying to get Laplacian using 3 different method, in case 1 and 2 results the same but what is wrong with 3?
Here is code in Matlab:
m= magic(6)
Lap1Dx= convn(m,[-1 2 -1],'same')
Lap1Dy= convn(m,[-1;2;-1],'same')
%ver1
Lap2Dxy= convn(m,[0 -1 0;-1 4 -1;0 -1 0],'same')
%ver2
Lap2Dxy= Lap1Dx+Lap1Dy %same as ver 1
%ver 3
%get laplacian using gradients
gradx= convn(m,[-1 1],'same')
grady= convn(m,[-1;1],'same')
gradxx= convn(gradx,[-1 1],'same')
gradyy= convn(grady,[-1;1],'same')
Lap2Dxy= gradxx+gradyy
Output:
m =
35 1 6 26 19 24
3 32 7 21 23 25
31 9 2 22 27 20
8 28 33 17 10 15
30 5 34 12 14 16
4 36 29 13 18 11
Lap1Dx =
69 -39 -15 27 -12 29
-26 54 -39 12 0 27
53 -15 -27 15 12 13
-12 15 21 -9 -12 20
55 -54 51 -24 0 18
-28 39 9 -21 12 4
Lap1Dy =
67 -30 5 31 15 23
-60 54 6 -6 0 6
51 -42 -36 6 21 0
-45 42 30 0 -21 -6
48 -54 6 -6 0 6
-22 67 24 14 22 6
Lap2Dxy =
136 -69 -10 58 3 52
-86 108 -33 6 0 33
104 -57 -63 21 33 13
-57 57 51 -9 -33 14
103 -108 57 -30 0 24
-50 106 33 -7 34 10
Lap2Dxy =
136 -69 -10 58 3 52
-86 108 -33 6 0 33
104 -57 -63 21 33 13
-57 57 51 -9 -33 14
103 -108 57 -30 0 24
-50 106 33 -7 34 10
gradx =
34 -5 -20 7 -5 24
-29 25 -14 -2 -2 25
22 7 -20 -5 7 20
-20 -5 16 7 -5 15
25 -29 22 -2 -2 16
-32 7 16 -5 7 11
grady =
32 -31 -1 5 -4 -1
-28 23 5 -1 -4 5
23 -19 -31 5 17 5
-22 23 -1 5 -4 -1
26 -31 5 -1 -4 5
4 36 29 13 18 11
gradxx =
39 15 -27 12 -29 24
-54 39 -12 0 -27 25
15 27 -15 -12 -13 20
-15 -21 9 12 -20 15
54 -51 24 0 -18 16
-39 -9 21 -12 -4 11
gradyy =
60 -54 -6 6 0 -6
-51 42 36 -6 -21 0
45 -42 -30 0 21 6
-48 54 -6 6 0 -6
22 -67 -24 -14 -22 -6
4 36 29 13 18 11
Lap2Dxy =
99 -39 -33 18 -29 18
-105 81 24 -6 -48 25
60 -15 -45 -12 8 26
-63 33 3 18 -20 9
76 -118 0 -14 -40 10
-35 27 50 1 14 22
First of all, there are some sign issues. Convolving with [-1 1] gives you the first derivative with the minus sign: convolution flips one of two arrays so you end subtracting each element from the following one. Convolve with [1 -1] to get the first derivative. For the second derivative, use [1 -2 1].
But the main issue here has to do with truncation ('same' parameter). With the first two approaches, you convolve and then truncate. With the third, you convolve, then truncate, then convolve again and truncate again.
Since the issue can be seen already on a one-dimensional array, I'll focus on the first row of yours. Let's drop the parameter same for now:
m = [35 1 6 26 19 24]
mx = convn(m,[1 -1]) // [35 -34 5 20 -7 5 -24]
mxx = convn(mx,[1 -1]) // [35 -69 39 15 -27 12 -29 24]
m2x = convn(m,[1 -2 1]) // [35 -69 39 15 -27 12 -29 24]
As you can see, the results are identical. Next, with the 'same' parameter:
mx = convn(m,[1 -1],'same') // [-34 5 20 -7 5 -24]
mxx = convn(mx,[1 -1],'same') // [39 15 -27 12 -29 24]
m2x = convn(m,[1 -2 1],'same') // [-69 39 15 -27 12 -29]
For m2x, 'same' cleanly picks the middle part of the full convolution, which is exactly the part you want.
But for the first-order derivative, there is just one element to drop. The choice has to be made, and convn drops the first one (resulting in the forward difference). When computing mxx, it has to do it again. So, as a result of dropping the first element of full convolution twice, you end up with a shifted array. This is why mxx has most of the same numbers as m2x, except misaligned.
If you insist on having two-step convolution (first derivative, then second), the first convolution must be without any truncation. Otherwise, the truncation influences the result of the second convolution. For the second one, you can use 'same' but in addition, the last element will need to be dropped. Like this.
mx = convn(m, [1 -1])
mxx = convn(mx,[1 -1],'same')
mxx = mxx(1:end-1)
Or, equivalently,
mx = convn(m, [1 -1])
mxx = convn(mx,[1 -1])
mxx = mxx(2:end-1)