Map column data to matching rows - vba

I have a sheet like this:
| A | B | C | D | E | F | G | H | ...
---------------------------------
| a | 1 | | b | 2 | | c | 7 |
---------------------------------
| b | 2 | | c | 8 | | b | 4 |
---------------------------------
| c |289| | a | 3 | | a |118|
---------------------------------
| d | 6 | | e | 3 | | e |888|
---------------------------------
| e | 8 | | d |111| | d |553|
---------------------------------
I want the sheet to become like this:
| A | B | C | D | E | F | G | H | ...
---------------------------------
| a | 1 | 3 |118| | | | |
---------------------------------
| b | 2 | 2 | 4 | | | | |
---------------------------------
| c |289| 8 | 7 | | | | |
---------------------------------
| d | 6 |111|553| | | | |
---------------------------------
| e | 8 | 3 |888| | | | |
---------------------------------
Col A, Col B and Col G have letters which are unique, and in the col next to it it has weights.
To make it even more clear,
| A | B |
---------
| a | 1 |
---------
| b | 2 |
---------
| c |289|
...
are the weights of a,b,c... in January
Similarly | D | E | are weights of a,b,c... in July and | G | H | are weights of a,b,c... in December
I need to put them side-by-side for comparison, the thing is they are NOT in order.
How do I approach this?
UPDATE
There are thousands of a,b,c, aa, bb, cc, aaa, avb, as, saf, sfa etc.. and some of them MAY be present in January (Col A) and not in July (Col D)

Something like this
code
Sub Squeeze()
[c1:c5] = Application.Index([E1:E5], Evaluate("IF(A1:A5<>"""",MATCH(A1:A5,D1:D5,0),A1:A5)"), 1)
[d1:d5] = Application.Index([H1:h5], Evaluate("IF(A1:A5<>"""",MATCH(A1:A5,G1:G5,0),A1:A5)"), 1)
[e1:h5].ClearContents
End Sub
Explanation of first line
Application.Index([E1:E5], Evaluate("IF(A1:A5<>"""",MATCH(A1:A5,D1:D5,0),A1:A5)"), 1)
The MATCH returns a VBA array matching the positions (5) of A1:A5 against D1:D5
INDEX then returns the corresponding values from E1:E5
So to use the key column of A1:A100 against M1:100 with values in N1:100
Application.Index([N1:N100], Evaluate("IF(A1:A100<>"""",MATCH(A1:A100,M1:M100,0),A1:A100)"), 1)

Extend as necessary: Sort D:E by D ascending, sort G:H by G ascending, delete G,F,D,C. If you want VBA, do this with Record Macro selected.

Related

Pandas Pivot-Table Containing List

I'd like to create a pivot table with the counts of values in a list, filtered by another column but am not sure how to use pandas pivot table (or function) with a list.
Here's an example what I'd like to do:
| Col1 | Col2 |
| --- | ----------- |
| A | ["e", "f"] |
| B | ["g", "f"] |
| C | ["g", "h"] |
| A | ["e", "g"] |
| B | ["g", "f"] |
| C | ["g", "e"] |
Ideal Pivot Table
| 1 | 2 |count|
| A | e | 2 |
| | f | 1 |
| | g | 1 |
| B | g | 2 |
| | f | 2 |
| C | g | 2 |
| | h | 1 |
| | e | 1 |
I cannot use a list to make a pivot table and am struggling to figure out how to modify the data or find a different method. Any help would be much appreciated!
Try this:
cols = ['Col1','Col2']
df.explode('Col2').groupby(cols).size()

Merge groups if they contain the same value

I have the following table:
+-----+----+---------+
| grp | id | sub_grp |
+-----+----+---------+
| 10 | A2 | 1 |
| 10 | B4 | 2 |
| 10 | F1 | 2 |
| 10 | B3 | 3 |
| 10 | C2 | 4 |
| 10 | A2 | 4 |
| 10 | H4 | 5 |
| 10 | K0 | 5 |
| 10 | Z3 | 5 |
| 10 | F1 | 5 |
| 10 | A1 | 5 |
| 10 | A | 6 |
| 10 | B | 6 |
| 10 | B | 7 |
| 10 | C | 7 |
| 10 | C | 8 |
| 10 | D | 8 |
| 20 | A | 1 |
| 20 | B | 1 |
| 20 | B | 2 |
| 20 | C | 2 |
| 20 | C | 3 |
| 20 | D | 3 |
+-----+----+---------+
Within every grp, my goal is to merge all the sub_grp sharing at least one id.
More than 2 sub_grp can be merged together.
The expected result should be:
+-----+----+---------+
| grp | id | sub_grp |
+-----+----+---------+
| 10 | A2 | 1 |
| 10 | B4 | 2 |
| 10 | F1 | 2 |
| 10 | B3 | 3 |
| 10 | C2 | 1 |
| 10 | A2 | 1 |
| 10 | H4 | 2 |
| 10 | K0 | 2 |
| 10 | Z3 | 2 |
| 10 | F1 | 2 |
| 10 | A1 | 2 |
| 10 | A | 6 |
| 10 | B | 6 |
| 10 | B | 6 |
| 10 | C | 6 |
| 10 | C | 6 |
| 10 | D | 6 |
| 20 | A | 1 |
| 20 | B | 1 |
| 20 | B | 1 |
| 20 | C | 1 |
| 20 | C | 1 |
| 20 | D | 1 |
+-----+----+---------+
Here is a SQL Fiddle with the test values: http://sqlfiddle.com/#!9/13666c/2
I am trying to solve this either with a stored procedure or queries.
This is an evolution from my previous problem: Merge rows containing same values
My understanding of the problem
Merge sub_grp (for a given grp) if any one of the IDs in one sub_grp match any one of the IDs in another sub_grp. A given sub_grp can be merged with only one other (the earliest in ascending order) sub_grp.
Disclaimer
This code may work. Not tested as OP did not provide DDLs and data scripts.
Solution
UPDATE final
SET sub_grp = new_sub_grp
FROM
-- For each grp, sub_grp combination return a matching new_sub_grp
( SELECT a.grp, a.sub_grp, MatchGrp.sub_grp AS new_sub_grp
FROM tbl AS a
-- Inner join will exclude cases where there are no matching sub_grp and thus nothing to update.
INNER JOIN
-- Find the earliest (if more than one sub-group is a match) matching sub-group where one of the IDs matches
( SELECT TOP 1 grp, sub_grp
FROM tbl AS b
-- b.sub_grp > a.sub_grp - this will only look at the earlier sub-groups avoiding the "double linking"
WHERE b.grp = a.grp AND b.sub_grp > a.sub_grp AND b.ID = a.ID
ORDER BY grp, sub_grp ) AS MatchGrp ON 1 = 1
-- Only return one record per grp, sub_grp combo
GROUP BY grp, sub_grp, MatchGrp.sub_grp ) AS final
You can re-number sub groups afterwards as a separate update statement with the help of DENSE_RANK window function.

get X number of non null columns as single string

I am trying to find the SQL command to do something but I don't know how to explain it so I'll use an example. I have a table like so:
| one | two | three | four |
|-----|-----|-------|------|
| a | h | i | j |
| b | k | l | |
| c | m | n | o |
| d | p | | |
| e | q | | |
| f | r | s | |
| g | t | | |
I need to create new columns that take the first non-null column from the right and kind of reverse it going up and joining/concatenating the fields.
| one | 1-up | 2-up | 3-up |
|-----|------|------|---------|
| a | j | j, i | j, i, h |
| b | l | l, k | |
| c | o | o, n | o, n, m |
| d | p | | |
| e | q | | |
| f | s | s, r | |
| g | t | | |
For b, since column four doesn't have data it uses three as the first value. Same for the other rows.
I hope this makes sense. I'm not sure how else to explain this.
You can use COALESCE like this :
select one, COALESCE(four,three,two,'') as '1-up',
COALESCE(four+','+three,three+','+two,'') as '2-up',
COALESCE(four+','+three+','+two,'') as '3-up'
from Table1
SQL Fiddle link Here

QBO3 Matrix "All" values

I have a QBO3 Matrix with a single Investor input, and a bunch of outputs:
| Investor | Output1 | Output2 | Output3 |
| -------- | ------- | ------- | ------- |
| 1,2,3 | A | B | C |
| 4,5 | D | E | F |
| 6,7,8 | D | B | G |
...
We now want to add a new "LoanType" input.
Is there a way to have the investor Code field count for ‘ALL’?
If I leave the field blank and select ‘Do Not Match’ will that apply to anything but NULL in that field?
Is there a way to only account for the Investors already listed in the Matrix without having to list them all in the Investor Code field?
TLDR; use input weights to get your desired result.
Assuming you added a LoanType input with the following rows:
| Investor | LoanType | Output1 | Output2 | Output3 |
| -------- | -------- | ------- | ------- | ------- |
| 1,2,3 | | A | B | C | row 1
| 4,5 | | D | E | F | row 2
| 6,7,8 | | D | B | G | row 3
| | Jumbo | H | I | F | row 4
...
Is there a way to have the Investor field count for 'ALL'?
Yes. Row 4 has no Investor specified, so all Jumbo loans would consider row 4 a possible match, regardless of investor.
If I leave the field blank and select ‘Do Not Match’ will that apply
to anything but NULL in that field?
Yes, but this is not recommended. If you do this, it will match a NULL investor, and it will match any "valid" investor value. This is equivalent to simply not entering anything at all.
Is there a way to only account for the Investors already listed in the
Matrix without having to list them all in the Investor Code field?
Yes, using weights.
I'm inferring from your question that if you have:
Investor = 27 (where Investor 27 does not appear in any other rows in the Matrix)
LoanType = "Jumbo"
you want row 4, but if you have:
Investor = 8 (where Investor 8 appears in another row in the Matrix), and
LoanType = "Jumbo"
you want to match row 3.
If this assumption is correct, you just need to set the Investor input weight to be higher than the LoanType weight. For example:
Investor.Weight = 10
LoanType.Weight = 5
In this scenario, given the inputs Investor = 8 and LoanType = "Jumbo", you would have:
| Investor | LoanType | Output1 | Output2 | Output3 | Weight |
| -------- | -------- | ------- | ------- | ------- | ------ |
| 6,7,8 | | D | B | G | 10 |
| | Jumbo | H | I | F | 5 |
| 1,2,3 | | A | B | C | 0 |
| 4,5 | | D | E | F | 0 |
...
Thus, your Investor match outweighs your LoanType match.
Lastly, if you had a rare use case where Investor = 2 and LoanType = "Jumbo" should result in Jumbo results, you can just add a row for that use case:
| Investor | LoanType | Output1 | Output2 | Output3 |
| -------- | -------- | ------- | ------- | ------- |
| 1,2,3 | | A | B | C | row 1
| 4,5 | | D | E | F | row 2
| 6,7,8 | | D | B | G | row 3
| | Jumbo | H | I | F | row 4
| 2 | Jumbo | H | I | F | row 5
...

Join a table representing a "transfer" between two rows of another table

Sorry for the confusing title; however a description and illustration should hopefully clear it up.
Essentially, I have the table A representing instances of a transfer of an 'amount' between rows of table B. I wish to join A with B so that I can display the details of the transfer:
================= A ===================
+-----+-----------+----------+--------+
| AID | fromID(FK) | toID(FK) | amount |
+-----+-----------+----------+--------+
| 1 | 1 | 5 | 100 |
| 2 | 1 | 3 | 150 |
| 3 | 5 | 3 | 500 |
| 4 | 1 | 5 | 200 |
| 5 | 4 | 5 | 800 |
| 6 | 3 | 5 | 15 |
+----+------------+----------+--------+
and
==== B =====
+----+------+
| BID | name |
+----+------+
| 1 | a |
| 2 | b |
| 3 | c |
| 4 | d |
| 5 | e |
+----+------+
I wish to join them and produce a "from name" column and a "to name" like:
+-----+------+----+--------+
| AID | from | to | amount |
+-----+------+----+--------+
| 1 | a | e | 100 |
| 2 | a | c | 150 |
| 3 | e | c | 500 |
| 4 | a | e | 200 |
| 5 | d | e | 800 |
| 6 | c | e | 15 |
+-----+------+----+--------+
You can join a on b twice:
SELECT aid, from_b.name, to_b.name, amount
FROM a
JOIN b from_b ON from_b.bid = a.fromid
JOIN b to_b ON to_b.bid = a.toid
Do a JOIN between the tables like below but you will have to join table B twice
select a.AID,
b.name as [from],
b1.name as [to],
a.amount
from A a
join B b on a.fromID(FK) = b.BID
join B b1 on a.toID(FK) = B.bid;
You can do this with out join.
Fiddle with sample data
select aid,
(select name from b where a.fromid = bid) as "from",
(select name from b where a.toid = bid) as "to",
amount
from a