Forum Discussion
Transforming rows to columns from a report output
I am trying to transform rows to columns from report output file(s).
[Original post is updated after BA_Pete reply, to cover duplicate medicine scenario]
I categorize as
- Page Row (1 row - Title row containing "Pharmacy Report")
- Header Rows (5 rows - "Patient ID" to "Pharmicist")
- Items (Dynamic rows - "Prescription Items:" to "Items Count:", can span multiple pages for a given "Patient ID" )
- Footer Rows (5 rows - "Prescription Date" to "Total Due")
Report data
001 Pharmacy Report Date: 01-MAR-2022 Page: 1
Patient ID: 100
Name: Brenda, Chad T
Address: 123, East 12th Avenue, City
Pharmacy: Ground floor pharmacy
Pharmacist: Nyugen Tony
Prescription Items:
Medicine: Tylenol 500mg
Dosage: 2.0
Usage: Morning and Daily 1 tablet after food
Medicine: Oflaxicin 250mg
Dosage: 1.5
Usage: Morning 1 and night 0.5
Items Count: 2
Prescription Date: 10-Feb-2022 10:30AM
Old Balance: $0.00
Bill Amount: $145.00
Bill Paid: $145.00
Total Due: $0.00
002 Pharmacy Report Date: 01-MAR-2022 Page: 1
Patient ID: 101
Name: Joleen, Wright T
Address: 123, East 23th Avenue, City
Pharmacy: Ground floor pharmacy
Pharmacist: Nyugen Tony
Prescription Items:
Medicine: Tylenol 500mg
Dosage: 2.0
Usage: Morning and Daily 1 tablet after food
Medicine: Bactrim DS 250mg
Dosage: 1.5
Usage: Morning 1 and night 0.5
Medicine: Thyorixn 50mcg
Dosage: 1.0
Usage: Morning 1.0
Medicine: Zyrtec
Dosage: 2.0
Usage: Morning 1 and night 1
003 Pharmacy Report Date: 01-MAR-2022 Page: 2
Patient ID: 101
Name: Joleen, Wright T
Address: 123, East 23th Avenue, City
Pharmacy: Ground floor pharmacy
Pharmacist: Nyugen Tony
Prescription Items:
Medicine: Hemarrhoid cream
Dosage: N/A
Usage: Apply after each bowel movement
Items Count: 5
Prescription Date: 11-Feb-2022 10:30AM
Old Balance: $0.00
Bill Amount: $245.00
Bill Paid: $0.00
Total Due: $245.00
004 Pharmacy Report Date: 01-MAR-2022 Page: 1
Patient ID: 102
Name: Paul, Peter Chan
Address: 123, East 12th Avenue, City
Pharmacy: Emergency pharmacy
Pharmacist: Alan Sheppard
Prescription Items:
Medicine: Tylenol 250mg
Dosage: 1.0
Usage: Morning and Daily 1 tablet after food
Medicine: Amox 250mg
Dosage: 1.5
Usage: Morning 1 and night 0.5
Items Count: 2
Prescription Date: 14-Feb-2022 02:30PM
Old Balance: $0.00
Bill Amount: $110.00
Bill Paid: $110.00
Total Due: $0.00
005 Pharmacy Report Date: 01-MAR-2022 Page: 1
Patient ID: 103
Name: Cathy, Peter Chan
Address: 123, East 12th Avenue, City
Pharmacy: Emergency pharmacy
Pharmacist: Alan Sheppard
Prescription Items:
Medicine: Tylenol 250mg
Dosage: 1.0
Usage: Morning and Daily 1 tablet after food
Medicine: Amox 250mg
Dosage: 1.5
Usage: Morning 1 and night 0.5
Medicine: Amox 250mg
Dosage: 1.5
Usage: Morning 1 and night 0.5
Items Count: 3
Prescription Date: 18-Feb-2022 02:30PM
Old Balance: $0.00
Bill Amount: $160.00
Bill Paid: $160.00
Total Due: $0.00
006 Pharmacy Report Date: 01-MAR-2022 Page: 1
Patient ID: 109
Name: Jack, Musk E
Address: 123, East 23th Avenue, City
Pharmacy: Ground floor pharmacy
Pharmacist: Nyugen Tony
Prescription Items:
Medicine: Tylenol 500mg
Dosage: 2.0
Usage: Morning and Daily 1 tablet after food
Medicine: Bactrim DS 250mg
Dosage: 1.5
Usage: Morning 1 and night 0.5
Medicine: Thyorixn 50mcg
Dosage: 1.0
Usage: Morning 1.0
Medicine: Bactrim DS 250mg
Dosage: 1.5
Usage: Morning 1 and night 0.5
007 Pharmacy Report Date: 01-MAR-2022 Page: 2
Patient ID: 109
Name: Jack, Musk E
Address: 123, East 23th Avenue, City
Pharmacy: Ground floor pharmacy
Pharmacist: Nyugen Tony
Prescription Items:
Medicine: Hemarrhoid cream
Dosage: N/A
Usage: Apply after each bowel movement
Medicine: Zyrtec
Dosage: 2.0
Usage: Morning 1 and night 1
Medicine: Tylenol 500mg
Dosage: 2.0
Usage: Morning and Daily 1 tablet after food
Items Count: 7
Prescription Date: 11-Feb-2022 10:30AM
Old Balance: $0.00
Bill Amount: $345.00
Bill Paid: $0.00
Total Due: $345.00
Rules:
* If Dosage is blank or N/A then chnage it to 1
Output expected: (or all data elements)
| Seq Number | Report Name | Date | Page | Patient ID | Name | Pharmacy | Medicine | Dosage | Items Count | Prescription Date | Bill Amount |
| 001 | Pharmacy Report | 01-MAR-2022 | 1 | 100 | Brenda, Chad T | Ground floor pharmacy | Tylenol 500mg | 2 | 2 | 10-Feb-2022 10:30AM | $145.00 |
| 001 | Pharmacy Report | 01-MAR-2022 | 1 | 100 | Brenda, Chad T | Ground floor pharmacy | Oflaxicin 250mg | 1.5 | 2 | 10-Feb-2022 10:30AM | $145.00 |
| 002 | Pharmacy Report | 01-MAR-2022 | 1 | 101 | Joleen, Wright T | Ground floor pharmacy | Tylenol 500mg | 2 | 5 | 11-Feb-2022 10:30AM | $245.00 |
| 002 | Pharmacy Report | 01-MAR-2022 | 1 | 101 | Joleen, Wright T | Ground floor pharmacy | Bactrim DS 250mg | 1.5 | 5 | 11-Feb-2022 10:30AM | $245.00 |
| 002 | Pharmacy Report | 01-MAR-2022 | 1 | 101 | Joleen, Wright T | Ground floor pharmacy | Thyorixn 50mcg | 1 | 5 | 11-Feb-2022 10:30AM | $245.00 |
| 002 | Pharmacy Report | 01-MAR-2022 | 1 | 101 | Joleen, Wright T | Ground floor pharmacy | Zyrtec | 2 | 5 | 11-Feb-2022 10:30AM | $245.00 |
| 003 | Pharmacy Report | 01-MAR-2022 | 2 | 101 | Joleen, Wright T | Ground floor pharmacy | Hemarrhoid cream | 1 | 5 | 11-Feb-2022 10:30AM | $245.00 |
| 004 | Pharmacy Report | 01-MAR-2022 | 1 | 102 | Paul, Peter Chan | Emergency pharmacy | Tylenol 250mg | 1 | 2 | 14-Feb-2022 02:30PM | $110.00 |
| 004 | Pharmacy Report | 01-MAR-2022 | 1 | 102 | Paul, Peter Chan | Emergency pharmacy | Amox 250mg | 1.5 | 2 | 14-Feb-2022 02:30PM | $110.00 |
| 005 | Pharmacy Report | 01-MAR-2022 | 1 | 103 | Cathy, Peter Chan | Emergency pharmacy | Tylenol 250mg | 1 | 3 | 18-Feb-2022 02:30PM | $160.00 |
| 005 | Pharmacy Report | 01-MAR-2022 | 1 | 103 | Cathy, Peter Chan | Emergency pharmacy | Amox 250mg | 1.5 | 3 | 18-Feb-2022 02:30PM | $160.00 |
| 005 | Pharmacy Report | 01-MAR-2022 | 1 | 103 | Cathy, Peter Chan | Emergency pharmacy | Amox 250mg | 1.5 | 3 | 18-Feb-2022 02:30PM | $160.00 |
| 006 | Pharmacy Report | 01-MAR-2022 | 1 | 109 | Jack, Musk E | Ground floor pharmacy | Tylenol 500mg | 2 | 7 | 11-Feb-2022 10:30AM | $345.00 |
| 006 | Pharmacy Report | 01-MAR-2022 | 1 | 109 | Jack, Musk E | Ground floor pharmacy | Bactrim DS 250mg | 1.5 | 7 | 11-Feb-2022 10:30AM | $345.00 |
| 006 | Pharmacy Report | 01-MAR-2022 | 1 | 109 | Jack, Musk E | Ground floor pharmacy | Thyorixn 50mcg | 1 | 7 | 11-Feb-2022 10:30AM | $345.00 |
| 006 | Pharmacy Report | 01-MAR-2022 | 1 | 109 | Jack, Musk E | Ground floor pharmacy | Bactrim DS 250mg | 1.5 | 7 | 11-Feb-2022 10:30AM | $345.00 |
| 007 | Pharmacy Report | 01-MAR-2022 | 2 | 109 | Jack, Musk E | Ground floor pharmacy | Hemarrhoid cream | 1 | 7 | 11-Feb-2022 10:30AM | $345.00 |
| 007 | Pharmacy Report | 01-MAR-2022 | 2 | 109 | Jack, Musk E | Ground floor pharmacy | Zyrtec | 2 | 7 | 11-Feb-2022 10:30AM | $345.00 |
| 007 | Pharmacy Report | 01-MAR-2022 | 2 | 109 | Jack, Musk E | Ground floor pharmacy | Tylenol 500mg | 2 | 7 | 11-Feb-2022 10:30AM | $345.00 |
19 rows
I started the power query as below:
let
Source = Table.FromRows(Json.Document(Binary.Decompress(Binary.FromText("7ZbdT9swEMD/lRPaY2G2Q2HrW0rZxqRCBZ0mDfFwJNfGwrEjx2Xkv8dN2pFWhM90YhN5iJK7i+/Dv9z5/HyLMQ6r1yhBm2JUwCllxrpSNkBHPWB8exiebgsmxNwOp17Gty4651sjdJK0g6OBlzBWyo4x9fq+JR1jBw4SjGFcKsI4tpTn3lIEHTjE3Pknl0B4TXpG3lS6olp1EUkPvloz0zFMlDEWsoW4biNz14PjYjYlDWOjK11l4H1FVmZOGg1HjtK8V8qHFMtIah/iuFCkjYIuY+m01A1MXiYndqpUflSvQ2O11FNAH8oApSqAg8NLRQ5w4sjCxJj4zvOdh5OJwpv5M4juug++073PBy+9aDlNHLCFSXkrU4ADXw+fsYCGRKsN42z7C11WG8ZZL2DhsDQ9UTH0UaGOvJG/PrCdxab1pVIQptXqcw3f7a7oRijjXgVKXTc2DhUMZrTU/VmxvDEmWqeM1yj7bhSR7sBPWxaskTMRtMHZ2wWtj5GzMoXBWQuk1QJPCmPljfaRp9H6ovdGvhSvLfSrsI6ip6Rej4rXQQpeBZL4L0D6RilamxgZQ2QJ05WCHn8MoV7RMMs8QBU3hFECl+Y3KUjNNaW+CA2dpftIZ+Gv7izigc7CGvtK7asFELutdxZRA2KEM9WBEc2r52eYfukEO0zJ+q32YTXSEPqywVlCWYY2fg4Oy75y3y/fTl/x+3bzt2fX7h1hTHjCRs+fXZw1zy7ezNj67Oq2TlhQI+wAXVK8I9YSYptlNniE2U+vZ3bvAWb3ns7sXuvMfq6PSYyuOjCc5Vdw+DZH5PtZq9X4FlTtt374+reo2szBq8Xz8eZ/gJV+uL/xU2LwolPi8quLWw==", BinaryEncoding.Base64), Compression.Deflate)), let _t = ((type nullable text) meta [Serialized.Text = true]) in type table [Line = _t]),
#"Changed Type" = Table.TransformColumnTypes(Source,{{"Line", type text}}),
#"Added Index" = Table.AddIndexColumn(#"Changed Type", "Index", 1, 1, Int64.Type),
#"Reordered Columns" = Table.ReorderColumns(#"Added Index",{"Index", "Line"}),
#"Added Conditional Column" = Table.AddColumn(#"Reordered Columns", "Section", each if Text.Contains([Line], "Page:") then "Page Row" else if Text.Contains([Line], "Patient ID")
then "Header Patient" else if Text.Contains([Line], "Prescription Items:") then "Items" else if Text.Contains([Line], "Prescription Date:") then "Footer" else if Text.Contains([Line], "Total Due:") then "End Patient" else null),
#"Sorted Rows" = Table.Sort(#"Added Conditional Column",{{"Index", Order.Ascending}}),
#"Duplicated Column" = Table.DuplicateColumn(#"Sorted Rows", "Section", "Section - Begin"),
#"Filtered Rows" = Table.SelectRows(#"Duplicated Column", each [Line] <> null and [Line] <> ""),
#"Filled Down" = Table.FillDown(#"Filtered Rows",{"Section"}),
#"Added Suffix" = Table.TransformColumns(#"Filled Down", {{"Section - Begin", each _ & " - Begin", type text}}),
#"Added Conditional Column1" = Table.AddColumn(#"Added Suffix", "Seq Number", each if [#"Section - Begin"] = "Page Row - Begin" then Text.Trim(Text.Start([Line], 5)) else null, type nullable text),
#"Filled Down1" = Table.FillDown(#"Added Conditional Column1",{"Seq Number"}),
#"Added Conditional Column2" = Table.AddColumn(#"Filled Down1", "Patient ID",
each if [#"Section - Begin"] = "Header Patient - Begin" then Text.Trim(Text.AfterDelimiter([Line], "Patient ID: ")) else null , type nullable text),
#"Filled Down2" = Table.FillDown(#"Added Conditional Column2",{"Patient ID"}),
#"Replaced Value" = Table.ReplaceValue(#"Filled Down2", each [Patient ID], each if [#"Section - Begin"] = "Page Row - Begin" then null else [Patient ID],Replacer.ReplaceValue,{"Patient ID"})
in
#"Replaced Value"
from here, not sure how to take further ...
trying to think to use group by using one or two columns and split ...
Any thoughts?
Hi sevenhills ,
I've gone about it a slightly different way from you i.e. I've built up all the columns from the original table, then filled them around the [medicine] and [dosage] columns (as these are the only ones that can be distinct within a group).
I think the addition of the STOP row could be slicker, maybe using a record function, but couldn't get my head in gear for it.
The only difference I got to your example output was on poor old Joleen's special cream, where your output stated a dosage of 1, but the data had N/A. Probably easy enough for you to add a step into my code to fix this if required.
let Source = Table.FromRows(Json.Document(Binary.Decompress(Binary.FromText("3ZXdb9owEMD/lRPaIzDbwAtvoem2ToJGLdWkoT6Y5CCW/BE5piP//YwDIyCyrSuapuUhSu4u9+Vf7haLDiEUTq8k51bxtIIHLIx1QRZzh2MgtDeNHnqMMLaz42svo53n7qKTcCdQO7iLvYSQIJtx5fUTizrjXbjJeQbzoIiyzGJZeks26MItL51/cjlEL6g36E2Fq2qv+0zG8NGajc5gJY2xUOzFTRtRujHMqs0aNcyNrnW1gY+VWlE4YTTcOVTlOMinmIlUaJ/ivJKojYQRIWoddLEpQ3GsX5fyVL9OjdVCr4H7VGIuZAUUHF9KdMBXDi2sjMmOkY8R7leSb3fPwEbnMWh/dCkGDVG0WOcOyN4k3EIJcOP74Stm0FJofWCU9D7gsj4wSsYDEk2D6b3MYMIl16k38tc70t8f2kRICZGqve80dDg60SVcZOMalKZubhyXEG/woPvhMdwIYVenjDYo+2wkou7CFxsa1soZG1yDs38XtAlPnRUK4scrkNZIPK+MFVvtM1fpudOLmR/EZ46+VtZh+julN7OiTZAGbwKJ/RcgfULFrc2NyCC1yNVJQ2fvI2h2NCoKD1DNDfI0h6X5hhKUeUHlm9AyWUa/mCz0zZOF/WSykNa50vhqD8Tw6pOFNYBI+EZ2IcFd9/wO03+6wW4VWn/UPq1WGiLfNnjMsSi4zV6Dw2GuXPrlrzNX/Llt//buGh4JI8wTlrx+d1HSvrtoO2O15vk7", BinaryEncoding.Base64), Compression.Deflate)), let _t = ((type nullable text) meta [Serialized.Text = true]) in type table [Line = _t]), addSeqID = Table.AddColumn(Source, "seqID", each if Text.Contains([Line], "Total Due:") then "STOP" else if Text.Contains([Line], "Page:") then Text.BeforeDelimiter([Line], " ") else null, type text), addReportName = Table.AddColumn(addSeqID, "reportName", each if Text.Contains([Line], "Total Due:") then "STOP" else if [seqID] <> null then Text.BetweenDelimiters([Line], [seqID], "Date:") else null, type text), addDate = Table.AddColumn(addReportName, "date", each if Text.Contains([Line], "Total Due:") then "STOP" else if [seqID] <> null then Text.BetweenDelimiters([Line], "Date:", "Page:") else null), addPage = Table.AddColumn(addDate, "page", each if Text.Contains([Line], "Total Due:") then "STOP" else if [seqID] <> null then Text.AfterDelimiter([Line], "Page:") else null, type text), addPatientID = Table.AddColumn(addPage, "patientID", each if Text.Contains([Line], "Total Due:") then "STOP" else if Text.Contains([Line], "Patient ID:") then Text.AfterDelimiter([Line], "Patient ID:") else null, type text), addName = Table.AddColumn(addPatientID, "name", each if Text.Contains([Line], "Total Due:") then "STOP" else if Text.Contains([Line], "Name:") then Text.AfterDelimiter([Line], "Name:") else null, type text), addPharmacy = Table.AddColumn(addName, "pharmacy", each if Text.Contains([Line], "Total Due:") then "STOP" else if Text.Contains([Line], "Pharmacy:") then Text.AfterDelimiter([Line], "Pharmacy:") else null, type text), addMedicine = Table.AddColumn(addPharmacy, "medicine", each if Text.Contains([Line], "Total Due:") or Text.Contains([Line], "Usage:") then "STOP" else if Text.Contains([Line], "Medicine:") then Text.AfterDelimiter([Line], "Medicine:") else null, type text), addDosage = Table.AddColumn(addMedicine, "dosage", each if Text.Contains([Line], "Total Due:") or Text.Contains([Line], "Usage:") then "STOP" else if Text.Contains([Line], "Dosage:") then Text.AfterDelimiter([Line], "Dosage:") else null), addItemsCount = Table.AddColumn(addDosage, "itemCount", each if Text.Contains([Line], "Total Due:") then "STOP" else if Text.Contains([Line], "Items Count:") then Text.AfterDelimiter([Line], "Items Count:") else null), addPrescDate = Table.AddColumn(addItemsCount, "prescriptionDate", each if Text.Contains([Line], "Total Due:") then "STOP" else if Text.Contains([Line], "Prescription Date:") then Text.AfterDelimiter([Line], "Prescription Date:") else null), addBillAmount = Table.AddColumn(addPrescDate, "billAmount", each if Text.Contains([Line], "Total Due:") then "STOP" else if Text.Contains([Line], "Bill Amount:") then Text.AfterDelimiter([Line], "Bill Amount:") else null), trimTextTypeCols = Table.TransformColumns(addBillAmount,{{"seqID", Text.Trim, type text}, {"reportName", Text.Trim, type text}, {"date", Text.Trim, type text}, {"page", Text.Trim, type text}, {"patientID", Text.Trim, type text}, {"name", Text.Trim, type text}, {"pharmacy", Text.Trim, type text}, {"medicine", Text.Trim, type text}}), remLineCol = Table.RemoveColumns(trimTextTypeCols,{"Line"}), fillDownToMedicine = Table.FillDown(remLineCol,{"seqID", "reportName", "date", "page", "patientID", "name", "pharmacy", "medicine"}), fillUpFromDosage = Table.FillUp(fillDownToMedicine,{"dosage", "itemCount", "prescriptionDate", "billAmount"}), filterMedStopNull = Table.SelectRows(fillUpFromDosage, each ([medicine] <> null and [medicine] <> "STOP")), tableDistinct = Table.Distinct(filterMedStopNull) in tableDistinctPete
- Anonymous4 years ago
try change at those points in the way you see in the pictures
regarding your questions about using the table.group function it takes some time to make a detailed explanation.
You can find somethink here
Hi BA_Pete
Final code:
Raw Datalet Source = Table.FromRows(Json.Document(Binary.Decompress(Binary.FromText("7ZbdT9swEMD/lRPaY2G2Q2HrW0rZxqRCBZ0mDfFwJNfGwrEjx2Xkv8dN2pFWhM90YhN5iJK7i+/Dv9z5/HyLMQ6r1yhBm2JUwCllxrpSNkBHPWB8exiebgsmxNwOp17Gty4651sjdJK0g6OBlzBWyo4x9fq+JR1jBw4SjGFcKsI4tpTn3lIEHTjE3Pknl0B4TXpG3lS6olp1EUkPvloz0zFMlDEWsoW4biNz14PjYjYlDWOjK11l4H1FVmZOGg1HjtK8V8qHFMtIah/iuFCkjYIuY+m01A1MXiYndqpUflSvQ2O11FNAH8oApSqAg8NLRQ5w4sjCxJj4zvOdh5OJwpv5M4juug++073PBy+9aDlNHLCFSXkrU4ADXw+fsYCGRKsN42z7C11WG8ZZL2DhsDQ9UTH0UaGOvJG/PrCdxab1pVIQptXqcw3f7a7oRijjXgVKXTc2DhUMZrTU/VmxvDEmWqeM1yj7bhSR7sBPWxaskTMRtMHZ2wWtj5GzMoXBWQuk1QJPCmPljfaRp9H6ovdGvhSvLfSrsI6ip6Rej4rXQQpeBZL4L0D6RilamxgZQ2QJ05WCHn8MoV7RMMs8QBU3hFECl+Y3KUjNNaW+CA2dpftIZ+Gv7izigc7CGvtK7asFELutdxZRA2KEM9WBEc2r52eYfukEO0zJ+q32YTXSEPqywVlCWYY2fg4Oy75y3y/fTl/x+3bzt2fX7h1hTHjCRs+fXZw1zy7ezNj67Oq2TlhQI+wAXVK8I9YSYptlNniE2U+vZ3bvAWb3ns7sXuvMfq6PSYyuOjCc5Vdw+DZH5PtZq9X4FlTtt374+reo2szBq8Xz8eZ/gJV+uL/xU2LwolPi8quLWw==", BinaryEncoding.Base64), Compression.Deflate)), let _t = ((type nullable text) meta [Serialized.Text = true]) in type table [Line = _t]), #"Changed Type" = Table.TransformColumnTypes(Source,{{"Line", type text}}), #"Added Index" = Table.AddIndexColumn(#"Changed Type", "Index", 1, 1, Int64.Type), #"Reordered Columns" = Table.ReorderColumns(#"Added Index",{"Index", "Line"}), #"Added Conditional Column" = Table.AddColumn(#"Reordered Columns", "Section", each if Text.Contains([Line], "Page:") then "Page Row" else if Text.Contains([Line], "Patient ID") then "Header Patient" else if Text.Contains([Line], "Prescription Items:") then "Items" else if Text.Contains([Line], "Items Count:") then "Footer" else if Text.Contains([Line], "Total Due:") then "End Patient" else null), #"Sorted Rows" = Table.Sort(#"Added Conditional Column",{{"Index", Order.Ascending}}), #"Duplicated Column" = Table.DuplicateColumn(#"Sorted Rows", "Section", "Section - Begin"), #"Filtered Rows" = Table.SelectRows(#"Duplicated Column", each [Line] <> null and [Line] <> ""), #"Filled Down" = Table.FillDown(#"Filtered Rows",{"Section"}), #"Added Suffix" = Table.TransformColumns(#"Filled Down", {{"Section - Begin", each _ & " - Begin", type text}}), #"Added Conditional Column1" = Table.AddColumn(#"Added Suffix", "Seq Number", each if [#"Section - Begin"] = "Page Row - Begin" then Text.Trim(Text.Start([Line], 5)) else null, type nullable text), #"Filled Down1" = Table.FillDown(#"Added Conditional Column1",{"Seq Number"}), #"Added Conditional Column2" = Table.AddColumn(#"Filled Down1", "Patient ID", each if [#"Section - Begin"] = "Header Patient - Begin" then Text.Trim(Text.AfterDelimiter([Line], "Patient ID: ")) else null , type nullable text), #"Filled Down2" = Table.FillDown(#"Added Conditional Column2",{"Patient ID"}), #"Replaced Value" = Table.ReplaceValue(#"Filled Down2", each [Patient ID], each if [#"Section - Begin"] = "Page Row - Begin" then null else [Patient ID],Replacer.ReplaceValue,{"Patient ID"}), #"Grouped Rows" = Table.Group(#"Replaced Value", {"Section", "Patient ID"}, {{"Rows Count", each Table.RowCount(_), Int64.Type}, {"First Row", each List.Min([Index]), type number}, {"End Row", each List.Max([Index]), type number}, {"TempTable100", each _, type table [Index=number, Line=nullable text, Section=text, #"Section - Begin"=text, Seq Number=nullable text, Patient ID=nullable text]}}) in #"Grouped Rows"Data Prep - Page Row
let Source = #"Raw Data", #"Filtered Rows" = Table.SelectRows(Source, each ([Section] = "Page Row")), #"Removed Other Columns" = Table.SelectColumns(#"Filtered Rows",{"TempTable100"}), #"Expanded TempTable100" = Table.ExpandTableColumn(#"Removed Other Columns", "TempTable100", {"Index", "Line", "Section", "Section - Begin", "Seq Number", "Patient ID"}, {"Index", "Line", "Section", "Section - Begin", "Seq Number", "Patient ID"}), #"Split Column by Positions" = Table.SplitColumn(#"Expanded TempTable100", "Line", Splitter.SplitTextByPositions({0, 18, 44, 58, 64}), {"Line.1", "Line.2", "Line.3", "Line.4", "Line.5"}), #"Replaced Value" = Table.ReplaceValue(#"Split Column by Positions"," Date: ","",Replacer.ReplaceText,{"Line.2"}), #"Removed Columns" = Table.RemoveColumns(#"Replaced Value",{"Line.4", "Section", "Section - Begin", "Patient ID", "Line.1"}), #"Renamed Columns" = Table.RenameColumns(#"Removed Columns",{{"Line.5", "Page"}, {"Line.3", "Date"}, {"Line.2", "Title"}}), #"Reordered Columns" = Table.ReorderColumns(#"Renamed Columns",{"Seq Number", "Index", "Title", "Date", "Page"}) in #"Reordered Columns"Data Prep - HF Rows
et Source = #"Raw Data", #"Filtered Rows" = Table.SelectRows(Source, each ([Section] <> "Page Row" and [Section] <> "Items" )), #"Removed Other Columns" = Table.SelectColumns(#"Filtered Rows",{"TempTable100"}), #"Expanded TempTable100" = Table.ExpandTableColumn(#"Removed Other Columns", "TempTable100", {"Index", "Line", "Section", "Section - Begin", "Seq Number", "Patient ID"}, {"Index", "Line", "Section", "Section - Begin", "Seq Number", "Patient ID"}), #"Split Column by Delimiter" = Table.SplitColumn(#"Expanded TempTable100", "Line", Splitter.SplitTextByDelimiter(": ", QuoteStyle.Csv), {"Line.1", "Line.2"}), #"Changed Type" = Table.TransformColumnTypes(#"Split Column by Delimiter",{{"Line.1", type text}, {"Line.2", type text}}), #"Removed Columns" = Table.RemoveColumns(#"Changed Type",{"Index", "Section", "Section - Begin", "Seq Number"}), #"Trimmed Text" = Table.TransformColumns(#"Removed Columns",{{"Line.1", Text.Trim, type text}, {"Line.2", Text.Trim, type text}}), #"Filtered Rows1" = Table.SelectRows(#"Trimmed Text", each ([Line.1] <> "Patient ID")), #"Removed Duplicates" = Table.Distinct(#"Filtered Rows1"), #"Pivoted Column" = Table.Pivot(#"Removed Duplicates", List.Distinct(#"Removed Duplicates"[Line.1]), "Line.1", "Line.2") in #"Pivoted Column"Data Prep - Items
let Source = #"Raw Data", #"Filtered Rows" = Table.SelectRows(Source, each ([Section] = "Items" )), #"Removed Other Columns" = Table.SelectColumns(#"Filtered Rows",{"TempTable100"}), #"Expanded TempTable100" = Table.ExpandTableColumn(#"Removed Other Columns", "TempTable100", {"Index", "Line", "Section", "Section - Begin", "Seq Number", "Patient ID"}, {"Index", "Line", "Section", "Section - Begin", "Seq Number", "Patient ID"}), #"Filtered Rows1" = Table.SelectRows(#"Expanded TempTable100", each ([Line] <> "Prescription Items:") and not Text.StartsWith([Line], "Usage")), #"Split Column by Delimiter" = Table.SplitColumn(#"Filtered Rows1", "Line", Splitter.SplitTextByDelimiter(": ", QuoteStyle.Csv), {"Line.1", "Line.2"}), #"Changed Type" = Table.TransformColumnTypes(#"Split Column by Delimiter",{{"Line.1", type text}, {"Line.2", type text}}), #"Removed Columns" = Table.RemoveColumns(#"Changed Type",{"Section", "Section - Begin"}), #"Trimmed Text" = Table.TransformColumns(#"Removed Columns",{{"Line.1", Text.Trim, type text}, {"Line.2", Text.Trim, type text}}), #"Added Conditional Column" = Table.AddColumn(#"Trimmed Text", "G1", each if [Line.1] = "Medicine" then "G1_L_" & Number.ToText([Index]) else if [Line.1] = "Items Count" then "G2_" & [Patient ID] else null), #"Sorted Rows" = Table.Sort(#"Added Conditional Column",{{"Index", Order.Ascending}}), #"Filled Down" = Table.FillDown(#"Sorted Rows",{"G1"}), #"Removed Columns1" = Table.RemoveColumns(#"Filled Down",{"Index"}), #"Pivoted Column" = Table.Pivot(#"Removed Columns1", List.Distinct(#"Removed Columns1"[Line.1]), "Line.1", "Line.2"), #"Replaced Value" = Table.ReplaceValue(#"Pivoted Column","N/A","1.0",Replacer.ReplaceText,{"Dosage"}) in #"Replaced Value"Data
let Source = Table.NestedJoin(#"Data Prep - HF Rows", {"Patient ID"}, #"Data Prep - Items", {"Patient ID"}, "Raw Data - Items", JoinKind.LeftOuter), #"Expanded Raw Data - Items" = Table.ExpandTableColumn(Source, "Raw Data - Items", {"Seq Number", "Medicine", "Dosage"}, {"Seq Number", "Medicine", "Dosage"}), #"Merged Queries" = Table.NestedJoin(#"Expanded Raw Data - Items", {"Seq Number"}, #"Data Prep - Page Row", {"Seq Number"}, "Raw Data - Page Row", JoinKind.LeftOuter), #"Expanded Raw Data - Page Row" = Table.ExpandTableColumn(#"Merged Queries", "Raw Data - Page Row", {"Title", "Date", "Page"}, {"Title", "Date", "Page"}), #"Reordered Columns" = Table.ReorderColumns(#"Expanded Raw Data - Page Row",{"Patient ID", "Name", "Items Count", "Prescription Date", "Medicine", "Dosage", "Old Balance", "Bill Amount", "Bill Paid", "Total Due", "Seq Number", "Title", "Date", "Page", "Address", "Pharmacy", "Pharmacist"}) in #"Reordered Columns"I will be happy to hear comments/updates/suggestions.
Converted as functions, used to process each file in the folder.
15 Replies
- BA_Pete
Super User
Hi sevenhills ,
I've gone about it a slightly different way from you i.e. I've built up all the columns from the original table, then filled them around the [medicine] and [dosage] columns (as these are the only ones that can be distinct within a group).
I think the addition of the STOP row could be slicker, maybe using a record function, but couldn't get my head in gear for it.
The only difference I got to your example output was on poor old Joleen's special cream, where your output stated a dosage of 1, but the data had N/A. Probably easy enough for you to add a step into my code to fix this if required.
let Source = Table.FromRows(Json.Document(Binary.Decompress(Binary.FromText("3ZXdb9owEMD/lRPaIzDbwAtvoem2ToJGLdWkoT6Y5CCW/BE5piP//YwDIyCyrSuapuUhSu4u9+Vf7haLDiEUTq8k51bxtIIHLIx1QRZzh2MgtDeNHnqMMLaz42svo53n7qKTcCdQO7iLvYSQIJtx5fUTizrjXbjJeQbzoIiyzGJZeks26MItL51/cjlEL6g36E2Fq2qv+0zG8NGajc5gJY2xUOzFTRtRujHMqs0aNcyNrnW1gY+VWlE4YTTcOVTlOMinmIlUaJ/ivJKojYQRIWoddLEpQ3GsX5fyVL9OjdVCr4H7VGIuZAUUHF9KdMBXDi2sjMmOkY8R7leSb3fPwEbnMWh/dCkGDVG0WOcOyN4k3EIJcOP74Stm0FJofWCU9D7gsj4wSsYDEk2D6b3MYMIl16k38tc70t8f2kRICZGqve80dDg60SVcZOMalKZubhyXEG/woPvhMdwIYVenjDYo+2wkou7CFxsa1soZG1yDs38XtAlPnRUK4scrkNZIPK+MFVvtM1fpudOLmR/EZ46+VtZh+julN7OiTZAGbwKJ/RcgfULFrc2NyCC1yNVJQ2fvI2h2NCoKD1DNDfI0h6X5hhKUeUHlm9AyWUa/mCz0zZOF/WSykNa50vhqD8Tw6pOFNYBI+EZ2IcFd9/wO03+6wW4VWn/UPq1WGiLfNnjMsSi4zV6Dw2GuXPrlrzNX/Llt//buGh4JI8wTlrx+d1HSvrtoO2O15vk7", BinaryEncoding.Base64), Compression.Deflate)), let _t = ((type nullable text) meta [Serialized.Text = true]) in type table [Line = _t]), addSeqID = Table.AddColumn(Source, "seqID", each if Text.Contains([Line], "Total Due:") then "STOP" else if Text.Contains([Line], "Page:") then Text.BeforeDelimiter([Line], " ") else null, type text), addReportName = Table.AddColumn(addSeqID, "reportName", each if Text.Contains([Line], "Total Due:") then "STOP" else if [seqID] <> null then Text.BetweenDelimiters([Line], [seqID], "Date:") else null, type text), addDate = Table.AddColumn(addReportName, "date", each if Text.Contains([Line], "Total Due:") then "STOP" else if [seqID] <> null then Text.BetweenDelimiters([Line], "Date:", "Page:") else null), addPage = Table.AddColumn(addDate, "page", each if Text.Contains([Line], "Total Due:") then "STOP" else if [seqID] <> null then Text.AfterDelimiter([Line], "Page:") else null, type text), addPatientID = Table.AddColumn(addPage, "patientID", each if Text.Contains([Line], "Total Due:") then "STOP" else if Text.Contains([Line], "Patient ID:") then Text.AfterDelimiter([Line], "Patient ID:") else null, type text), addName = Table.AddColumn(addPatientID, "name", each if Text.Contains([Line], "Total Due:") then "STOP" else if Text.Contains([Line], "Name:") then Text.AfterDelimiter([Line], "Name:") else null, type text), addPharmacy = Table.AddColumn(addName, "pharmacy", each if Text.Contains([Line], "Total Due:") then "STOP" else if Text.Contains([Line], "Pharmacy:") then Text.AfterDelimiter([Line], "Pharmacy:") else null, type text), addMedicine = Table.AddColumn(addPharmacy, "medicine", each if Text.Contains([Line], "Total Due:") or Text.Contains([Line], "Usage:") then "STOP" else if Text.Contains([Line], "Medicine:") then Text.AfterDelimiter([Line], "Medicine:") else null, type text), addDosage = Table.AddColumn(addMedicine, "dosage", each if Text.Contains([Line], "Total Due:") or Text.Contains([Line], "Usage:") then "STOP" else if Text.Contains([Line], "Dosage:") then Text.AfterDelimiter([Line], "Dosage:") else null), addItemsCount = Table.AddColumn(addDosage, "itemCount", each if Text.Contains([Line], "Total Due:") then "STOP" else if Text.Contains([Line], "Items Count:") then Text.AfterDelimiter([Line], "Items Count:") else null), addPrescDate = Table.AddColumn(addItemsCount, "prescriptionDate", each if Text.Contains([Line], "Total Due:") then "STOP" else if Text.Contains([Line], "Prescription Date:") then Text.AfterDelimiter([Line], "Prescription Date:") else null), addBillAmount = Table.AddColumn(addPrescDate, "billAmount", each if Text.Contains([Line], "Total Due:") then "STOP" else if Text.Contains([Line], "Bill Amount:") then Text.AfterDelimiter([Line], "Bill Amount:") else null), trimTextTypeCols = Table.TransformColumns(addBillAmount,{{"seqID", Text.Trim, type text}, {"reportName", Text.Trim, type text}, {"date", Text.Trim, type text}, {"page", Text.Trim, type text}, {"patientID", Text.Trim, type text}, {"name", Text.Trim, type text}, {"pharmacy", Text.Trim, type text}, {"medicine", Text.Trim, type text}}), remLineCol = Table.RemoveColumns(trimTextTypeCols,{"Line"}), fillDownToMedicine = Table.FillDown(remLineCol,{"seqID", "reportName", "date", "page", "patientID", "name", "pharmacy", "medicine"}), fillUpFromDosage = Table.FillUp(fillDownToMedicine,{"dosage", "itemCount", "prescriptionDate", "billAmount"}), filterMedStopNull = Table.SelectRows(fillUpFromDosage, each ([medicine] <> null and [medicine] <> "STOP")), tableDistinct = Table.Distinct(filterMedStopNull) in tableDistinctPete
- sevenhills
Super User
Thank you BA_Pete
--------
I updated the raw data and output in the original post(, helps the question in long term viewers.)
--------
Few points I missed in the original post, after looking into your solution.
a) In a given report file,
i) No patient id duplicates in a given file.
ii) For a given patient,
Medicine, Dosage, Usage combinations can have duplicates.
Medicine, Dosage, Usage will always stay as combination in a given page. (no split of pages of these three row combination)
b) I went back to the requirements and saw that if dosage is blank or N/A then change to 1. So, you are right, I can add a step to fix it. Sorry, I did not mention in the original post, as I was not aware when I did the first post.
--------
My each data file is upto 25000 lines, and get each week one file. Data combinations are many.
Let me check and get back to you, (will update this reply)
- sevenhills
Super User
I am thinking along these lines, for modularity
Work in progress
Raw Data - Step 1let Source = Table.FromRows(Json.Document(Binary.Decompress(Binary.FromText("7ZbdT9swEMD/lRPaY2G2Q2HrW0rZxqRCBZ0mDfFwJNfGwrEjx2Xkv8dN2pFWhM90YhN5iJK7i+/Dv9z5/HyLMQ6r1yhBm2JUwCllxrpSNkBHPWB8exiebgsmxNwOp17Gty4651sjdJK0g6OBlzBWyo4x9fq+JR1jBw4SjGFcKsI4tpTn3lIEHTjE3Pknl0B4TXpG3lS6olp1EUkPvloz0zFMlDEWsoW4biNz14PjYjYlDWOjK11l4H1FVmZOGg1HjtK8V8qHFMtIah/iuFCkjYIuY+m01A1MXiYndqpUflSvQ2O11FNAH8oApSqAg8NLRQ5w4sjCxJj4zvOdh5OJwpv5M4juug++073PBy+9aDlNHLCFSXkrU4ADXw+fsYCGRKsN42z7C11WG8ZZL2DhsDQ9UTH0UaGOvJG/PrCdxab1pVIQptXqcw3f7a7oRijjXgVKXTc2DhUMZrTU/VmxvDEmWqeM1yj7bhSR7sBPWxaskTMRtMHZ2wWtj5GzMoXBWQuk1QJPCmPljfaRp9H6ovdGvhSvLfSrsI6ip6Rej4rXQQpeBZL4L0D6RilamxgZQ2QJ05WCHn8MoV7RMMs8QBU3hFECl+Y3KUjNNaW+CA2dpftIZ+Gv7izigc7CGvtK7asFELutdxZRA2KEM9WBEc2r52eYfukEO0zJ+q32YTXSEPqywVlCWYY2fg4Oy75y3y/fTl/x+3bzt2fX7h1hTHjCRs+fXZw1zy7ezNj67Oq2TlhQI+wAXVK8I9YSYptlNniE2U+vZ3bvAWb3ns7sXuvMfq6PSYyuOjCc5Vdw+DZH5PtZq9X4FlTtt374+reo2szBq8Xz8eZ/gJV+uL/xU2LwolPi8quLWw==", BinaryEncoding.Base64), Compression.Deflate)), let _t = ((type nullable text) meta [Serialized.Text = true]) in type table [Line = _t]), #"Changed Type" = Table.TransformColumnTypes(Source,{{"Line", type text}}), #"Added Index" = Table.AddIndexColumn(#"Changed Type", "Index", 1, 1, Int64.Type), #"Reordered Columns" = Table.ReorderColumns(#"Added Index",{"Index", "Line"}), #"Added Conditional Column" = Table.AddColumn(#"Reordered Columns", "Section", each if Text.Contains([Line], "Page:") then "Page Row" else if Text.Contains([Line], "Patient ID") then "Header Patient" else if Text.Contains([Line], "Prescription Items:") then "Items" else if Text.Contains([Line], "Prescription Date:") then "Footer" else if Text.Contains([Line], "Total Due:") then "End Patient" else null), #"Sorted Rows" = Table.Sort(#"Added Conditional Column",{{"Index", Order.Ascending}}), #"Duplicated Column" = Table.DuplicateColumn(#"Sorted Rows", "Section", "Section - Begin"), #"Filtered Rows" = Table.SelectRows(#"Duplicated Column", each [Line] <> null and [Line] <> ""), #"Filled Down" = Table.FillDown(#"Filtered Rows",{"Section"}), #"Added Suffix" = Table.TransformColumns(#"Filled Down", {{"Section - Begin", each _ & " - Begin", type text}}), #"Added Conditional Column1" = Table.AddColumn(#"Added Suffix", "Seq Number", each if [#"Section - Begin"] = "Page Row - Begin" then Text.Trim(Text.Start([Line], 5)) else null, type nullable text), #"Filled Down1" = Table.FillDown(#"Added Conditional Column1",{"Seq Number"}), #"Added Conditional Column2" = Table.AddColumn(#"Filled Down1", "Patient ID", each if [#"Section - Begin"] = "Header Patient - Begin" then Text.Trim(Text.AfterDelimiter([Line], "Patient ID: ")) else null , type nullable text), #"Filled Down2" = Table.FillDown(#"Added Conditional Column2",{"Patient ID"}), #"Replaced Value" = Table.ReplaceValue(#"Filled Down2", each [Patient ID], each if [#"Section - Begin"] = "Page Row - Begin" then null else [Patient ID],Replacer.ReplaceValue,{"Patient ID"}) in #"Replaced Value"Raw Data Prep - Step 2
let Source = #"Raw Data - Step 1", addPageRowInfo = Table.AddColumn(Source, "Page Row Info", each if [#"Section - Begin"] = "Page Row - Begin" then Parse_PageRow([Line]) else null, type table), addPatientEndRowInfo = Table.AddColumn(addPageRowInfo, "addPageRowInfo", each if [#"Section - Begin"] = "End Patient - Begin" then Parse_PatientEndRow([Line]) else null, type table), #"Grouped Rows" = Table.Group(addPatientEndRowInfo, {"Seq Number", "Section"}, {{"Rows Count", each Table.RowCount(_), Int64.Type}, {"First Row", each List.Min([Index]), type number}, {"End Row", each List.Max([Index]), type number}, {"TempTable100", each _, type table [Index=number, Line=nullable text, Section=text, #"Section - Begin"=text, Seq Number=nullable text, Patient ID=nullable text, Page Row Info=table, addPageRowInfo=table]}}) in #"Grouped Rows"For each row, took the "TempTable100" column (and to modularize,) added these functions and later clubbed them all. it is the same logic we did for the whole file.
...
Function: Parse_HeaderPatientRows
Function: Parse_ItemsFunction: Parse_FooterRows
Function: Parse_PatientEndRow
= Table.AddColumn(#"Grouped Rows", "Data Cols Info", each
if [#"Section"] = "End Patient" then Parse_PatientEndRow([allrows]{0}[Line])
else if [#"Section"] = "Footer" then Parse_FooterRows([allrows])
else if [#"Section"] = "Header Patient" then Parse_HeaderPatientRows([allrows])
else if [#"Section"] = "Items" then Parse_Items([allrows])
else null
, type table)Note: All the functions code can be done as one function too. Because of lot more data in each row and few more transformations, I adapted as separate functions.
Thank you BA_Pete . Stopper row identification is a cool technique.
- AnonymousNot applicable
I don't know how different my idea is from those already proposed (they are too long to read 😁).
There is certainly something to clean or refine in this one.
If considered valid, you can work on it a little longer.- sevenhills
Super User
Thank you Anonymous
I am thinking along these lines
(The report file looks like detailed report output, which is similar to consolidated invoice / order details report for all customers ~ based on my oracle reports knowledge. but this is pharmacy domain)a) Load the file and add line number.
b) Categorize into 5 sections, as each may have one row (or) begin and end rows
- Section: Page Row ~ 1 row. Unique is "001", "002". To identify the begin of each page
- Section: Header Patient ~ Number of rows fixed. Repeats on every page.
- Section: Items ~ Dynamic section with variable number of rows and combinations. Spans across pages for a given patient.
- Section: Footer ~ Number of rows fixed. Repeats at the end of a given patient, which can be on the same page or in other page. It all depends on number of items.
- Section: End Patient ~ 1 row. To identify the end for each patient (Total Due: )
- which I borrowed the concept from BA_Pete
c) Modularizing by passing sections to the functions
Similar to the function you did
I started with these, In a given file,
- Uniqueness is Seq Number "001", "002", ... in each page. Added a column "Seq Number".
- "Patient ID", to identify the rows across one or multiple pages
- Similarly parse each of the above sections and club them back
Still working on it ...
- Club them back and remove unwanted rows and columns and present
Coming back to your solution, I don't know Spanish and tying to understand and adapt the concept.
- AnonymousNot applicable
I try to summarize the steps of my idea.
I have divided the report into groups identified by the "Total Due" line.
Since I used the Equation Criteria parameter of the Table.Group () function, I had to insert a dummy line of this type at the beginning of the report which becomes the first line of each group (not the last) but is eliminated with Table.Skip (). If this info is needed, it can be obtained from the Bill lines.
On each of these groups I have applied the trans () function.I am attaching a file with an application of the trans function to the more complex group (the one that has two different Pharmacy Reports).
The important part is the indexing by subgroups of the Patient Items part, so that you can pivot correctly.
I did some filldowns (but it might be useful to do some fillups too), before deleting the superfluous lines.Some cleaning and tidying up of the labels and column names remain to be done.
For example, since I used ":" to divide the initial text into columns, the term "Page" remained with the values of the "Line.2" field ie the date. It should be deleted from there and added to the column where the page number "Line.3" is.PS
My language is Italian not Spanish.
I don't know Spanish either 😁
- sevenhills
Super User
I am able to process the data and got what I needed.
In a given file, I classified as sections and processed as three segments.
a) For a given Patient ID, identify the static rows & sections
i.e., header, footer, end row. Use split by ": " and get the info needed
b) For a given Patient ID, identify the dynamic rows
i.e., items. Use sort, split by ": ", fill down and fill up.c) For a given Patient ID, identify the extended info,
i.e., rows with Seq Number, PAGE: , spanned across multiple pages and get info needed.Join ((a) + (b)) + (c)
Question I have to figure out is the best way to add these steps in order to process all files in the folder.
Thank you Anonymous , BA_Pete
- BA_Pete
Super User
Hi sevenhills ,
Any chance you can drop your final M with a JSON source in here please?
I'm interested to see/learn how you completed this in the end.
Ta,
Pete
- sevenhills
Super User
Hi BA_Pete
Final code:
Raw Datalet Source = Table.FromRows(Json.Document(Binary.Decompress(Binary.FromText("7ZbdT9swEMD/lRPaY2G2Q2HrW0rZxqRCBZ0mDfFwJNfGwrEjx2Xkv8dN2pFWhM90YhN5iJK7i+/Dv9z5/HyLMQ6r1yhBm2JUwCllxrpSNkBHPWB8exiebgsmxNwOp17Gty4651sjdJK0g6OBlzBWyo4x9fq+JR1jBw4SjGFcKsI4tpTn3lIEHTjE3Pknl0B4TXpG3lS6olp1EUkPvloz0zFMlDEWsoW4biNz14PjYjYlDWOjK11l4H1FVmZOGg1HjtK8V8qHFMtIah/iuFCkjYIuY+m01A1MXiYndqpUflSvQ2O11FNAH8oApSqAg8NLRQ5w4sjCxJj4zvOdh5OJwpv5M4juug++073PBy+9aDlNHLCFSXkrU4ADXw+fsYCGRKsN42z7C11WG8ZZL2DhsDQ9UTH0UaGOvJG/PrCdxab1pVIQptXqcw3f7a7oRijjXgVKXTc2DhUMZrTU/VmxvDEmWqeM1yj7bhSR7sBPWxaskTMRtMHZ2wWtj5GzMoXBWQuk1QJPCmPljfaRp9H6ovdGvhSvLfSrsI6ip6Rej4rXQQpeBZL4L0D6RilamxgZQ2QJ05WCHn8MoV7RMMs8QBU3hFECl+Y3KUjNNaW+CA2dpftIZ+Gv7izigc7CGvtK7asFELutdxZRA2KEM9WBEc2r52eYfukEO0zJ+q32YTXSEPqywVlCWYY2fg4Oy75y3y/fTl/x+3bzt2fX7h1hTHjCRs+fXZw1zy7ezNj67Oq2TlhQI+wAXVK8I9YSYptlNniE2U+vZ3bvAWb3ns7sXuvMfq6PSYyuOjCc5Vdw+DZH5PtZq9X4FlTtt374+reo2szBq8Xz8eZ/gJV+uL/xU2LwolPi8quLWw==", BinaryEncoding.Base64), Compression.Deflate)), let _t = ((type nullable text) meta [Serialized.Text = true]) in type table [Line = _t]), #"Changed Type" = Table.TransformColumnTypes(Source,{{"Line", type text}}), #"Added Index" = Table.AddIndexColumn(#"Changed Type", "Index", 1, 1, Int64.Type), #"Reordered Columns" = Table.ReorderColumns(#"Added Index",{"Index", "Line"}), #"Added Conditional Column" = Table.AddColumn(#"Reordered Columns", "Section", each if Text.Contains([Line], "Page:") then "Page Row" else if Text.Contains([Line], "Patient ID") then "Header Patient" else if Text.Contains([Line], "Prescription Items:") then "Items" else if Text.Contains([Line], "Items Count:") then "Footer" else if Text.Contains([Line], "Total Due:") then "End Patient" else null), #"Sorted Rows" = Table.Sort(#"Added Conditional Column",{{"Index", Order.Ascending}}), #"Duplicated Column" = Table.DuplicateColumn(#"Sorted Rows", "Section", "Section - Begin"), #"Filtered Rows" = Table.SelectRows(#"Duplicated Column", each [Line] <> null and [Line] <> ""), #"Filled Down" = Table.FillDown(#"Filtered Rows",{"Section"}), #"Added Suffix" = Table.TransformColumns(#"Filled Down", {{"Section - Begin", each _ & " - Begin", type text}}), #"Added Conditional Column1" = Table.AddColumn(#"Added Suffix", "Seq Number", each if [#"Section - Begin"] = "Page Row - Begin" then Text.Trim(Text.Start([Line], 5)) else null, type nullable text), #"Filled Down1" = Table.FillDown(#"Added Conditional Column1",{"Seq Number"}), #"Added Conditional Column2" = Table.AddColumn(#"Filled Down1", "Patient ID", each if [#"Section - Begin"] = "Header Patient - Begin" then Text.Trim(Text.AfterDelimiter([Line], "Patient ID: ")) else null , type nullable text), #"Filled Down2" = Table.FillDown(#"Added Conditional Column2",{"Patient ID"}), #"Replaced Value" = Table.ReplaceValue(#"Filled Down2", each [Patient ID], each if [#"Section - Begin"] = "Page Row - Begin" then null else [Patient ID],Replacer.ReplaceValue,{"Patient ID"}), #"Grouped Rows" = Table.Group(#"Replaced Value", {"Section", "Patient ID"}, {{"Rows Count", each Table.RowCount(_), Int64.Type}, {"First Row", each List.Min([Index]), type number}, {"End Row", each List.Max([Index]), type number}, {"TempTable100", each _, type table [Index=number, Line=nullable text, Section=text, #"Section - Begin"=text, Seq Number=nullable text, Patient ID=nullable text]}}) in #"Grouped Rows"Data Prep - Page Row
let Source = #"Raw Data", #"Filtered Rows" = Table.SelectRows(Source, each ([Section] = "Page Row")), #"Removed Other Columns" = Table.SelectColumns(#"Filtered Rows",{"TempTable100"}), #"Expanded TempTable100" = Table.ExpandTableColumn(#"Removed Other Columns", "TempTable100", {"Index", "Line", "Section", "Section - Begin", "Seq Number", "Patient ID"}, {"Index", "Line", "Section", "Section - Begin", "Seq Number", "Patient ID"}), #"Split Column by Positions" = Table.SplitColumn(#"Expanded TempTable100", "Line", Splitter.SplitTextByPositions({0, 18, 44, 58, 64}), {"Line.1", "Line.2", "Line.3", "Line.4", "Line.5"}), #"Replaced Value" = Table.ReplaceValue(#"Split Column by Positions"," Date: ","",Replacer.ReplaceText,{"Line.2"}), #"Removed Columns" = Table.RemoveColumns(#"Replaced Value",{"Line.4", "Section", "Section - Begin", "Patient ID", "Line.1"}), #"Renamed Columns" = Table.RenameColumns(#"Removed Columns",{{"Line.5", "Page"}, {"Line.3", "Date"}, {"Line.2", "Title"}}), #"Reordered Columns" = Table.ReorderColumns(#"Renamed Columns",{"Seq Number", "Index", "Title", "Date", "Page"}) in #"Reordered Columns"Data Prep - HF Rows
et Source = #"Raw Data", #"Filtered Rows" = Table.SelectRows(Source, each ([Section] <> "Page Row" and [Section] <> "Items" )), #"Removed Other Columns" = Table.SelectColumns(#"Filtered Rows",{"TempTable100"}), #"Expanded TempTable100" = Table.ExpandTableColumn(#"Removed Other Columns", "TempTable100", {"Index", "Line", "Section", "Section - Begin", "Seq Number", "Patient ID"}, {"Index", "Line", "Section", "Section - Begin", "Seq Number", "Patient ID"}), #"Split Column by Delimiter" = Table.SplitColumn(#"Expanded TempTable100", "Line", Splitter.SplitTextByDelimiter(": ", QuoteStyle.Csv), {"Line.1", "Line.2"}), #"Changed Type" = Table.TransformColumnTypes(#"Split Column by Delimiter",{{"Line.1", type text}, {"Line.2", type text}}), #"Removed Columns" = Table.RemoveColumns(#"Changed Type",{"Index", "Section", "Section - Begin", "Seq Number"}), #"Trimmed Text" = Table.TransformColumns(#"Removed Columns",{{"Line.1", Text.Trim, type text}, {"Line.2", Text.Trim, type text}}), #"Filtered Rows1" = Table.SelectRows(#"Trimmed Text", each ([Line.1] <> "Patient ID")), #"Removed Duplicates" = Table.Distinct(#"Filtered Rows1"), #"Pivoted Column" = Table.Pivot(#"Removed Duplicates", List.Distinct(#"Removed Duplicates"[Line.1]), "Line.1", "Line.2") in #"Pivoted Column"Data Prep - Items
let Source = #"Raw Data", #"Filtered Rows" = Table.SelectRows(Source, each ([Section] = "Items" )), #"Removed Other Columns" = Table.SelectColumns(#"Filtered Rows",{"TempTable100"}), #"Expanded TempTable100" = Table.ExpandTableColumn(#"Removed Other Columns", "TempTable100", {"Index", "Line", "Section", "Section - Begin", "Seq Number", "Patient ID"}, {"Index", "Line", "Section", "Section - Begin", "Seq Number", "Patient ID"}), #"Filtered Rows1" = Table.SelectRows(#"Expanded TempTable100", each ([Line] <> "Prescription Items:") and not Text.StartsWith([Line], "Usage")), #"Split Column by Delimiter" = Table.SplitColumn(#"Filtered Rows1", "Line", Splitter.SplitTextByDelimiter(": ", QuoteStyle.Csv), {"Line.1", "Line.2"}), #"Changed Type" = Table.TransformColumnTypes(#"Split Column by Delimiter",{{"Line.1", type text}, {"Line.2", type text}}), #"Removed Columns" = Table.RemoveColumns(#"Changed Type",{"Section", "Section - Begin"}), #"Trimmed Text" = Table.TransformColumns(#"Removed Columns",{{"Line.1", Text.Trim, type text}, {"Line.2", Text.Trim, type text}}), #"Added Conditional Column" = Table.AddColumn(#"Trimmed Text", "G1", each if [Line.1] = "Medicine" then "G1_L_" & Number.ToText([Index]) else if [Line.1] = "Items Count" then "G2_" & [Patient ID] else null), #"Sorted Rows" = Table.Sort(#"Added Conditional Column",{{"Index", Order.Ascending}}), #"Filled Down" = Table.FillDown(#"Sorted Rows",{"G1"}), #"Removed Columns1" = Table.RemoveColumns(#"Filled Down",{"Index"}), #"Pivoted Column" = Table.Pivot(#"Removed Columns1", List.Distinct(#"Removed Columns1"[Line.1]), "Line.1", "Line.2"), #"Replaced Value" = Table.ReplaceValue(#"Pivoted Column","N/A","1.0",Replacer.ReplaceText,{"Dosage"}) in #"Replaced Value"Data
let Source = Table.NestedJoin(#"Data Prep - HF Rows", {"Patient ID"}, #"Data Prep - Items", {"Patient ID"}, "Raw Data - Items", JoinKind.LeftOuter), #"Expanded Raw Data - Items" = Table.ExpandTableColumn(Source, "Raw Data - Items", {"Seq Number", "Medicine", "Dosage"}, {"Seq Number", "Medicine", "Dosage"}), #"Merged Queries" = Table.NestedJoin(#"Expanded Raw Data - Items", {"Seq Number"}, #"Data Prep - Page Row", {"Seq Number"}, "Raw Data - Page Row", JoinKind.LeftOuter), #"Expanded Raw Data - Page Row" = Table.ExpandTableColumn(#"Merged Queries", "Raw Data - Page Row", {"Title", "Date", "Page"}, {"Title", "Date", "Page"}), #"Reordered Columns" = Table.ReorderColumns(#"Expanded Raw Data - Page Row",{"Patient ID", "Name", "Items Count", "Prescription Date", "Medicine", "Dosage", "Old Balance", "Bill Amount", "Bill Paid", "Total Due", "Seq Number", "Title", "Date", "Page", "Address", "Pharmacy", "Pharmacist"}) in #"Reordered Columns"I will be happy to hear comments/updates/suggestions.
Converted as functions, used to process each file in the folder.
- AnonymousNot applicable
- sevenhills
Super User
Thank you.
I have been using the same for long time in many projects. As I used mockup data for this post, actual files has lot more combinations. Working on it for nested functions / transformation at each file level and clubbing them. 🙂