Insert a row for each array item -- for many arrays - postgresql

Say you have a table:
CREATE TABLE tab (
x integer,
y integer,
);
And multiple rows of the form x, [y1, y2, ...]:
x ys
1 [2,3]
4 [5,6,7]
There might be an arbitrary but non-zero (or non-nil) amount of ys in each row. x always non-nil.
How to insert such that you end up with a row for each x, y pair, that is:
x y
1 2
1 3
4 5
4 6
4 7

You can unnest() the arrays:
insert into tab (x,y)
select v.x, t.y
from (
values
(1, array[2,3]),
(4, array[5,6,7])
) as v(x,ya)
cross join unnest(v.ya) as t(y)
Online example

Related

T-SQL - Padding data points

I have a series of data points in a table in an Azure SQL database. When plotted, the points have the following distribution:
What I am trying to do is add padding so that they have the appearance of being in a continuous line - or at lease more continuous than it is now.
I tried adding points between each point, but the problem with that is that it's all relative. If you look closely, you can see some of the points are blue, and some are dark red. The blue points are the ones I added, but the line looks the same.
I'm looking for advice on the logic I should use to solve this issue. I want to add x number of points between each data point based on the distance between the nearest points... if that makes sense.
I think this works
declare #t table (x smallmoney primary key, y smallmoney);
declare #inc smallmoney = 1;
insert into #t(x, y) values
(1, 1)
, (5, 3)
, (8, 4)
, (10, 5)
, (11, 6);
with cte as
( select x, x as x0, y, y as y0, cnt = cast(1 as smallmoney)
, lead(x) over (order by x) as nextX
, lead(y) over (order by x) as nextY
from #t t
union all
select x + #inc, x0, y + #inc/(nextX-x0)*(nextY-y0), y0, cnt+1, nextX, nextY
from cte t
where x + #inc < nextX
)
select *
from cte t
order by t.x;
I'm not confident that this is the best solution, but I think you could build off of it. Here's an sqlfiddle
SELECT x + COALESCE(((nextx-x)/10)*inc, 0) as x, y + COALESCE(((nexty-y)/10)*inc, 0) as y
FROM
(SELECT x, y, nextx, nexty, inc.n + 0.0 as inc FROM
(SELECT x, y, lead(x) over (order by x) as nextx, lead(y) over (order by x) as nexty
FROM points) p inner join (VALUES(0),(1),(2),(3),(4),(5),(6),(7),(8),(9)) inc(n)
ON nextx is not null or inc.n = 0
) a ORDER BY x
This will add 9 points between each point (10 points total, including the "real" one).
The basic idea is that I'm using lead for each row to get the next x and next y, then I join that to a hardcoded list of values 0 to 9. Then for each value, I increment x by 1/10 of the difference between nextx and x, and increment y by 1/10 of the difference between nexty and y.
The join condition nextx is not null or inc.n = 0 is so that I only join inc(0) to the last x value (rather than joining 10 times).
You could change my hardcoded list of values and the hardcoded 10s to increment differently. Similarly, you'd probably need some changes if you only want integers, but the principle will be the same.

using ismember and indexing to get values from one vector to another

I have 2 vectors of Ids, lets call them a_id (10000 x 1) & b_id (4500 x 1).
I also have a vectors of numerical values, lets calls it b_num (4500 x 1). The values in b_num correspond to the id's in b_id.
Lastly I have a vector lets call it a_num which is (10000 x 1) which has no values in it. I want to populate it with the values from b_num where the id's match in a_id and b_id.
a_id b_id b_num
ADA BHN 2
PLB ADA 4
BHN LMK 3
LMK
So the result would look something like below,
a_id a_num
ADA 4
PLB 0
BHN 2
LMK 3
I am trying to use ismember but with out much joy.
a_num = NaN * ones(length(a_id(:, 1)), 1);
[found, pos] = ismember(a_id, b_id);
a_num(found(found~=0), 1) = b_num(pos(pos~=0), 1);
The result is
a_id a_num
ADA 4
PLB 2
BHN 3
LMK NaN
Firstly, you can use NaN with input arguments the same as ones, so initialise your a_num using
a_num = NaN(size(a_id)); % Use size instead of length for clarity. length(x) = max(size(x))
Getting ismember to work
We can find all indices of a_id within b_id and assign like so
[bfound, idx] = ismember(a_id, b_id); % Find a_id members in b_id
a_num(bfound) = b_num(idx(bfound)); % Assign matched values
% output [4 NaN 2 3]
Using intersect
You can avoid some logical indexing by using the index arguments of intersect. After initialising a_num the same...
[~, a_idx, b_idx] = intersect(a_id, b_id); % indices of b in a and a in b
a_num(a_idx) = b_num(b_idx);
% output [4 NaN 2 3]
Is this what you want? ismember seems to work fine. I think you were just miss using the found part of your code (idxa in mine). That is a logical array so you did not need to do this found(found~=0)
>> a_id ={ 'ADA' 'PLB' 'BHN' 'LMK'};
>> b_id = {'BHN' 'ADA' 'LMK'};
>> b_num = [2 4 3];
>> [idxa idxb] = ismember(a_id,b_id)
idxa =
1 0 1 1
idxb =
2 0 1 3
>> a_num = zeros(size(a_id));
>> a_num(idxa) = b_num(idxb(idxb~=0))
a_num =
4 0 2 3
Note: replace zeros() with nan() in the initialization of a_num if you prefer that instead.

sorting an array and keep the index in MATLAB

I have an array called A, it has 2 Rows and 56 columns ( as shown in the attached image)
the first row represents the values while the second row represents the index for each value.
I want to sort the first row but keep the index for each value (I tried to use Sort function but it doesnt keep the index of each value in the second row).
for example, let's suppose I have this matrix
input
x=[9 2 4 3 ;3 1 8 2 ]
i want the output looks like this
y=[2 3 4 9; 1 2 8 3 ];
There are a couple of ways to achieve this:
[y1,I] = sort(x(1,:))
y2 = x(2,I)
y = [ y1 ; y2 ]
This basically sort the first row of your data, and save the sorting index in I, and then use those index to get the 'sorted' second row, and then just join them
or
y = sortrows(x')'
The ' operator transposes the matrix, which allows you to use sortrows and then use it again to reshape your output matrix.
You can use indices of sorted elements
[S,I]=sort(x(1,:));
result = [S;x(2,I)]
The first row is sorted and indices of the sorted elements is used to order the second row.
result
2 3 4 9
1 2 8 3
You can get the indices directly from sorting the first row. These can be used as an argument in x itself:
x=[9 2 4 3 ;3 1 8 2 ];
%get the indices of the first row:
[~,indices] = sort(x(1,:))
%return x in the order "indices" for each row:
y = [x(1,indices);x(2,indices)]

Delete a row and a column at the same time in a matrix

Is it possible to delete a row and a column of a matrix at the same time ?
Example:
M = magic(3)
M =
8 1 6
3 5 7
4 9 2
And with a one-liner remove the column 2 and the row 2 to obtain:
M =
8 6
4 2
You can supply an array of indices for the row and column indices and MATLAB will automatically return all permutations (usually an annoying feature, but a benefit here).
So you could do something like:
M([1 3], [1 3])
Or more flexible, you could use logical indexing
M([true false true], [true false true])
Or more generally (with potentially more rows and columns)
row = 2; % Could be an array of rows to exclude
column = 2; % Could be an array of columns to exclude
out = M(~ismember(1:size(M, 1), row), ~ismember(1:size(M, 2), column))

Merge tables Without sorting on Keys

I have two tables (or actually I have several) that should be merged using innerjoin. It works as expected; except that it sort on the "Keys", which destroys my data since it actually must be arranged in the original row order.
From the help section:
C is sorted by the values in the key variables
t1 = Case Val
3 1
1 1
2 1
t2 = Val2 Case Subset
2 2 2
1 2 2
2 3 1
tnew = innerjoin(t1,t2)
tnew = Case Val Val2 Subset
2 ...
% will start with "2" since its a lower value than "3", but in t1 "3" was in lower row than "2", it is rearranged
How should I avoid the sorting? Hopeless to use innerjoin?
In addition to the resulting table, innerjoin returns two extra outputs: The row indices of the first table and the row indices of the second table that correspond to the rows in the output.
You can simply use the second output to determine the rows in t1 that were used and you could sort these. Then use the sort order to change the ordering of the rows in the result of the join.
%// Setup the data
t1 = table([3;1;2], [1;1;1], 'VariableNames', {'Case', 'Val'});
t2 = table([2;1;2],[2;2;3],[2;2;1], 'VariableNames', {'Val2', 'Case', 'Subset'});
%// Perform the inner join and keep track of where the rows were in t1
[tnew, rows_in_t1] = innerjoin(t1, t2);
%// Sort them in order to maintain the order in t1
[~, sortinds] = sort(rows_in_t1);
%// Apply this sort order to the new table
tnew = tnew(sortinds,:);
%// Case Val Val2 Subset
%// ____ ___ ____ ______
%// 3 1 2 1
%// 2 1 2 2
%// 2 1 1 2