Power BI Desktop DAX khởi động lại cột tổng


9

Tôi có một bảng nơi mỗi người có một kỷ lục cho mỗi ngày trong năm. Tôi đã sử dụng chức năng này để đạt được tổng số hoạt động dựa trên cột số dư hàng ngày

CALCULATE(
SUM(Leave[Daily Balance]),
FILTER(
   ALLEXCEPT(Leave, Leave[Employee Id]),
   Leave[Date] <= EARLIER(Leave[Date])
))

nhưng tôi cần tổng số chạy để khởi động lại từ 1 nếu Loại = Hoạt động VÀ tổng số dư của Số dư hàng ngày nhỏ hơn 0 VÀ Loại hàng trước không bằng Hoạt động. Dưới đây là ảnh chụp màn hình từ Excel. Cột chức năng cần thiết là những gì tôi cần để có được.

nhập mô tả hình ảnh ở đây


1
Trên hàng cho ngày 5 tháng 11, Người 1, giả sử dữ liệu thử nghiệm của chúng tôi có loại trống. 'Hàm bắt buộc' sẽ trả về 1 hoặc 2 vào ngày 6 tháng 11?
Ryan B.

Nó sẽ trả về 2 cho ngày 6 tháng 11. Việc "đặt lại" sẽ không xảy ra vì ngày 5 tháng 11 sẽ là 1 (không phải là số âm). Cảm ơn cho bài viết chi tiết của bạn. Tôi đang xem xét ngày hôm nay
LynseyC

Câu trả lời:


1

Đây không chỉ là một tổng số đang chạy với một điều kiện, mà còn là một tổng thể được lồng / cụm, vì logic phải được áp dụng ở cấp độ ID. Đối với các bảng lớn, M tốt hơn so với DAX, vì nó không sử dụng nhiều RAM. (Tôi đã viết về điều đó ở đây: Liên kết đến Blogpost

Hàm sau điều chỉnh logic đó cho trường hợp hiện tại và phải được áp dụng ở cấp ID: (Tên cột bắt buộc là: "Loại", "Trợ cấp hàng ngày", "Điều chỉnh")

(MyTable as table) => let SelectJustWhatsNeeded = Table.SelectColumns(MyTable,{"Type", "Daily Allowance", "Adjustments"}), ReplaceNulls = Table.ReplaceValue(SelectJustWhatsNeeded,null,0,Replacer.ReplaceValue,{"Adjustments"}), #"Merged Columns" = Table.CombineColumns(ReplaceNulls,{"Daily Allowance", "Adjustments"}, List.Sum,"Amount"), TransformToList = List.Buffer(Table.ToRecords(#"Merged Columns")), ConditionalRunningTotal = List.Skip(List.Generate( () => [Type = TransformToList{0}[Type], Result = 0, Counter = 0], each [Counter] <= List.Count(TransformToList), each [ Result = if TransformToList{[Counter]}[Type] = "working" and [Result] < 0 and [Type] <> "working" then TransformToList{[Counter]}[Amount] else TransformToList{[Counter]}[Amount] + [Result] , Type = TransformToList{[Counter]}[Type], Counter = [Counter] + 1 ], each [Result] )), Custom1 = Table.FromColumns( Table.ToColumns(MyTable) & {ConditionalRunningTotal}, Table.ColumnNames(MyTable) & {"Result"} ) in Custom1


Điều này đã giải quyết vấn đề. Hoạt động hoàn hảo và không làm chậm báo cáo. Cảm ơn
LynseyC

5

Tổng quat

Đây là một điều khó khăn để yêu cầu PowerBI làm, vì vậy cách tiếp cận gọn gàng có thể khó tìm.

Vấn đề lớn nhất là mô hình dữ liệu của PowerBI không hỗ trợ khái niệm kiểm đếm hoạt động - ít nhất không phải là cách chúng tôi làm trong Excel. Trong Excel, một cột có thể tham chiếu các giá trị xảy ra trong 'hàng trước' của cùng cột đó và sau đó được điều chỉnh bởi một số 'thay đổi hàng ngày' được liệt kê trong một cột khác.

PowerBI chỉ có thể bắt chước điều này bằng cách thêm tất cả các thay đổi hàng ngày qua một số tập hợp con của các hàng. Chúng tôi lấy giá trị ngày trong hàng hiện tại của chúng tôi và tạo một bảng được lọc trong đó tất cả các ngày nhỏ hơn ngày của hàng hiện tại này và sau đó tổng hợp tất cả các thay đổi hàng ngày từ tập hợp con đó. Điều này có vẻ là một sự khác biệt tinh tế, nhưng nó khá quan trọng:

Điều này có nghĩa là không có cách nào để 'ghi đè' tổng số hoạt động của chúng tôi. Phép toán duy nhất đang được thực hiện là xảy ra trên cột chứa các thay đổi hàng ngày - cột chứa 'tổng chạy' chỉ là kết quả - nó không bao giờ được sử dụng trong bất kỳ phép tính nào của hàng tiếp theo.

Chúng ta phải từ bỏ khái niệm 'đặt lại' và thay vào đó hãy tưởng tượng tạo một cột chứa giá trị 'điều chỉnh'. Điều chỉnh của chúng tôi sẽ là một giá trị có thể được bao gồm để khi các điều kiện được mô tả được đáp ứng, tổng số dư và điều chỉnh hàng ngày sẽ tổng bằng 1.

Nếu chúng ta nhìn vào hoạt động được tính toán do OP đưa ra, chúng ta sẽ thấy rằng giá trị của tổng số hoạt động của chúng ta vào một ngày 'không làm việc' ngay trước một ngày 'làm việc' mang lại cho chúng ta số tiền cần thiết, nếu đảo ngược, sẽ bằng 0 và làm cho tổng số chạy vào mỗi ngày làm việc tiếp theo tăng thêm một. Đây là hành vi mong muốn của chúng tôi (với một vấn đề sẽ được mô tả sau này).

Kết quả

nhập mô tả hình ảnh ở đây

Most Recent Date Prior to Work = 

CALCULATE(
Max(Leave[Date]),
FILTER(
   ALLEXCEPT(Leave, Leave[Id]),
   Leave[Date] = EARLIER(Leave[Date]) -1 && Leave[Type] <> "Working" && Earlier(Leave[Type]) = "Working"
))

Nó giúp biết được sự khác biệt giữa bối cảnh hàng và bộ lọc và cách EARLIER vận hành để thực hiện theo tính toán này. Trong kịch bản này, bạn có thể nghĩ "EARLIER" có nghĩa là 'tham chiếu này trỏ đến giá trị trong hàng hiện tại "và nếu không thì một tham chiếu đến toàn bộ bảng được trả về bởi" ALLEXCEPT (Rời khỏi, để lại [Id]). " cách này, chúng tôi tìm thấy những nơi mà hàng hiện tại có loại "Hoạt động" và hàng của ngày trước có một số loại khác.

Most Recent Date Prior to Work Complete = 

CALCULATE(
Max(Leave[Most Recent Date Prior to Work]),
FILTER(
   ALLEXCEPT(Leave, Leave[Id]),
   Leave[Date] <= EARLIER(Leave[Date])
))

Tính toán này bắt chước một loại hoạt động 'điền xuống'. Nó nói, "Khi nhìn vào tất cả các hàng có ngày trước ngày trên hàng NÀY, hãy trả về giá trị lớn nhất trong 'Ngày gần đây nhất trước khi làm việc."

Daily Balance Adjustment = 

CALCULATE(
SUM(Leave[Running Daily Balance]),
FILTER(
   ALLEXCEPT(Leave, Leave[Id]),
   Leave[Date] = EARLIER(Leave[Most Recent Date Prior to Work Complete])
))

Bây giờ mỗi hàng có một trường giải thích nơi cần tìm số dư hàng ngày để sử dụng làm điều chỉnh của chúng tôi, chúng tôi có thể chỉ cần tìm nó từ bảng.

Adjusted Daily Balance = Leave[Running Daily Balance] - Leave[Daily Balance Adjustment]

Và cuối cùng chúng tôi áp dụng điều chỉnh cho tổng số chạy của chúng tôi cho kết quả cuối cùng.

Vấn đề

Cách tiếp cận này không giải quyết được rằng không nên thiết lập lại trừ khi số dư hàng ngày đang chạy dưới 0. Tôi đã được chứng minh là sai trước đây, nhưng tôi sẽ nói rằng điều này không thể được thực hiện trong DAX một mình vì nó tạo ra sự phụ thuộc vòng tròn. Về cơ bản, bạn đưa ra một yêu cầu: sử dụng giá trị tổng hợp để xác định những gì cần được đưa vào tổng hợp.

Vì vậy, đó là xa tôi có thể mang lại cho bạn. Hy vọng nó giúp.


1
Về điểm cuối cùng của bạn, tôi tin rằng bạn là chính xác. DAX không thể thực hiện đệ quy.
Alexis Olson

3

Hy vọng lần sau bạn sẽ dán một csv hoặc mã tạo dữ liệu mẫu thay vì hình ảnh. :)

Thay vào đó, hãy để tôi đề nghị bạn thực hiện các tính toán của mình trong PowerQuery. Tôi đã cố gắng tách mã trong vài bước để cải thiện khả năng đọc. Điều này có thể trông phức tạp hơn một chút, tuy nhiên hoạt động tốt. Chỉ cần dán nó vào trình chỉnh sửa nâng cao và sau đó thay thế nguồn bằng dữ liệu nguồn của bạn. May mắn nhất!

let
    Source = Table.FromRows(Json.Document(Binary.Decompress(Binary.FromText("i45WMjDUMzDSMzIwtFTSUQpILSrOz1MwBDLL84uyM/PSlWJ1gGqMsKuBSBrjkzQhwnRTItSYEaHGHJ9DLPBJWhI23dAAjwGGOAIRIokj9OCmxwIA", BinaryEncoding.Base64), Compression.Deflate)), let _t = ((type text) meta [Serialized.Text = true]) in type table [date = _t, name = _t, #"type" = _t]),
    SetTypes = Table.TransformColumnTypes(Source,{{"date", type date}, {"name", type text}, {"type", type text}}),
    TempColumn1 = Table.AddColumn(SetTypes, "LastOtherType", (row)=>List.Max(Table.SelectRows(SetTypes, each ([name] = row[name] and [type] <> row[type] and [date] <= row[date]))[date], row[date]), type date) //Here for each row we select all rows of other type with earlier date, and take max that date. Thus we know when was previous change from one type to another
 //Here for each row we select all rows of other type with earlier date, and take max that date. Thus we know when was previous change from one type to another
,
    TempColumn2 = Table.AddColumn(TempColumn1, "Count", (row)=>
(if row[type]="working" then 1 else -1) * 
Table.RowCount(
Table.SelectRows(SetTypes, each ([name] = row[name] and [type] = row[type] and [date] <= row[date] and [date] > row[LastOtherType])) /* select all rows between type change (see prev step) and current row */
), /*and count them*/
Int64.Type) // finally multiply -1 if they are not working type
,
    FinalColumn = Table.AddColumn(TempColumn2, "FinalFormula", (row)=> 
(if row[type] = "working" then row[Count] else /* for working days use Count, for others take prev max Count and add current Count, which is negative for non-working*/
Table.LastN(Table.SelectRows(TempColumn2, each [name] = row[name] and [type] = "working" and [LastOtherType] <= row[LastOtherType]),1)[Count]{0}
+ row[Count])
, Int64.Type),
    RemovedTempColumns = Table.RemoveColumns(FinalColumn,{"LastOtherType", "Count"})
in
    RemovedTempColumns

Tôi không chắc điều này bao gồm mọi kịch bản, nhưng có vẻ như là cách tiếp cận đúng.
Mike Honey

Tôi chỉ có thể làm việc này nếu loại đầu tiên cho mỗi người là Làm việc. Cũng như với các ví dụ DAX, nó khởi động lại việc đánh số cho một phong trào Làm việc khi tổng tích lũy cho hàng trước đó là một số dương. Tôi đoán hình ảnh của tôi đã gây hiểu nhầm vì nó chỉ chứa kịch bản này. Tôi nên có một thời gian khi loại thay đổi để làm việc nhưng tổng số hàng trước đó là tích cực.
LynseyC

@LynseyC tốt, tất nhiên, mã này không phải là giải pháp hoàn hảo và đầy đủ, mà là một ví dụ về các phương pháp có thể được sử dụng. Chỉ cần sửa đổi nếu cho kịch bản của bạn.
Eugene

@LynseyC cũng vậy, một trong những lợi thế khi thực hiện phép toán này trong PowerQuery chứ không phải DAX là một cách dễ dàng để giữ các cột tạm thời khỏi mô hình dữ liệu.
Eugene

3

Tôi nghĩ rằng tôi có nó!

Đây là kết quả, dựa trên giải pháp tôi đã đăng trước đó: (Dữ liệu đã được sửa đổi để thể hiện nhiều hành vi "làm việc / không làm việc" và các trường hợp sử dụng)

KẾT QUẢ

nhập mô tả hình ảnh ở đây

CHI TIẾT

(1) Bỏ các Colums "Điều chỉnh số dư hàng ngày đã điều chỉnh" và "Điều chỉnh số dư hàng ngày". Chúng ta sẽ nhận được kết quả tương tự với một bước ít hơn chỉ trong chốc lát.

(2) Tạo cột sau (RDB = "chạy số dư hàng ngày") ...

Grouped RDB = 

CALCULATE(
SUM(Leave[Daily Balance]),
FILTER(
   ALLEXCEPT(Leave, Leave[Id], Leave[Most Recent Date Prior to Work Complete]),
   Leave[Date] <= EARLIER(Leave[Date]) 
))

Đã tạo ra "Ngày gần đây nhất trước khi hoàn thành công việc", chúng tôi thực sự có phần cần thiết để thực hiện 'thiết lập lại' mà tôi tuyên bố là không thể trước đây. Bằng cách lọc trên trường này, chúng tôi có cơ hội bắt đầu mỗi lát cắt ở '1'

(3) Chúng tôi vẫn có cùng một vấn đề tho, chúng tôi không thể nhìn vào kết quả trong cột của chúng tôi và sử dụng nó để quyết định những việc cần làm sau này trong cùng cột đó. Nhưng chúng ta CÓ THỂ xây dựng một cột điều chỉnh mới sẽ chứa thông tin đó! Và chúng tôi đã có một tài liệu tham khảo về 'Ngày gần đây nhất trước khi đi làm' - đó là ngày cuối cùng trong nhóm trước đó ... hàng với thông tin chúng tôi cần!

Grouped RDB Adjustment = 

VAR CalculatedAdjustment =
CALCULATE(
SUM(Leave[Grouped RDB]),
FILTER(
   ALLEXCEPT(Leave, Leave[Id]),
   Leave[Date] IN SELECTCOLUMNS(
        FILTER(
            Leave,
            Leave[Most Recent Date Prior to Work] <> BLANK() &&
            Leave[id] = EARLIER(Leave[Id])), "MRDPtW", Leave[Most Recent Date Prior to Work]) &&
   Leave[Most Recent Date Prior to Work Complete] < EARLIER(Leave[Most Recent Date Prior to Work Complete]) &&
   Leave[Most Recent Date Prior to Work Complete] <> Blank()
))

RETURN if (CalculatedAdjustment > 0, CalculatedAdjustment, 0)

Vì vậy, chúng tôi nhìn vào ngày cuối cùng trong mỗi nhóm trước đó và nếu tổng số các điều chỉnh đó có giá trị dương, chúng tôi sẽ áp dụng nó và nếu nó âm thì chúng tôi sẽ để nó một mình. Ngoài ra, nếu vài ngày đầu tiên của chúng tôi là những ngày không làm việc, chúng tôi không muốn chút tiêu cực ban đầu đó trong sự điều chỉnh của mình để nó cũng bị lọc đi.

(4) Bước cuối cùng này sẽ đưa điều chỉnh vào kết quả cuối cùng. Tổng hợp hai cột mới và cuối cùng chúng ta sẽ có Số dư hàng ngày được điều chỉnh. Voila!

Adjusted Running Daily Balance = Leave[Grouped RDB] + Leave[Grouped RDB Adjustment]

Chúng tôi đã xây dựng rất nhiều cột bổ sung dọc theo kết quả này thường không phải là điều tôi thích làm. Nhưng, đây là một điều khó khăn.


Xin chào @Ryan B. Điều này hoạt động hoàn hảo cho hơn 200 người trong tổ chức của tôi nhưng một người không hoạt động. Tôi đã cố gắng tự thay đổi mã nhưng tôi không thể có bất cứ điều gì để giải quyết vấn đề. Tôi nghĩ rằng đó là bởi vì họ đã làm việc một thời gian dài và sau đó làm việc chỉ một ngày trước khi có thêm thời gian nghỉ. Tôi đã liên kết với một hình ảnh để hiển thị vấn đề. Cảm ơn hình ảnh
LynseyC

Tôi đã sửa đổi biện pháp "Điều chỉnh RDB được nhóm" để nó có thể vượt qua các khoản tích lũy lớn trong nhiều chu kỳ "làm việc / không làm việc".
Ryan B.

2
Xin chào, cảm ơn vì tất cả những nỗ lực, đánh giá cao. Thật không may, sửa đổi đã không giải quyết vấn đề. Tuy nhiên, nếu tôi loại bỏ điều kiện cuối cùng trong bộ lọc "Để lại [Ngày gần đây nhất trước khi hoàn thành công việc] <> Trống ()" thì nó đã giải quyết vấn đề nhưng sau đó nó lại phá vỡ calcs của người ban đầu :-(
LynseyC

Bắn. Vâng, tôi hy vọng bạn có thể tìm thấy một cái gì đó hoạt động.
Ryan B.

2

Mất một lúc, nhưng tôi đã có thể đưa ra một cách giải quyết. Giả sử, giá trị cân bằng cho các khoảng trống luôn là -1 và giá trị là 1 cho "Làm việc" và dữ liệu đó có sẵn cho tất cả các ngày không có khoảng cách, một cái gì đó như phép tính dưới đây có thể hoạt động:

Running Total = 
    VAR Employee = Leave[Employee ID]
    VAR Date1 = Leave[Date]
    VAR Prev_Blank = CALCULATE(MAX(Leave[Date]),
                        FILTER(Leave,Leave[Date] < Date1),
                        FILTER(Leave,Leave[Employee ID]=Employee),
                        FILTER(Leave,Leave[Type]=BLANK()))  
    VAR Day_count_Working = CALCULATE(COUNT(Leave[Date]),
                        FILTER(Leave,Leave[Date] > Prev_Blank),
                        FILTER(Leave,Leave[Date] <= Date1),
                        FILTER(Leave,Leave[Employee ID]=Employee),
                        FILTER(Leave,Leave[Type]="Working")) 
    VAR Day_count = CALCULATE(COUNT(Leave[Date]),
                        FILTER(Leave,Leave[Date] >= Prev_Blank),
                        FILTER(Leave,Leave[Date] <= Date1),
                        FILTER(Leave,Leave[Employee ID]=Employee)) 
RETURN (IF(Day_count_Working=BLANK(),Day_count,Day_count-1)-Day_count_Working)*-1 + Day_count_Working

Hãy nhớ rằng, đây có thể không phải là một sản phẩm hoàn chỉnh như tôi đã làm việc với một mẫu nhỏ, nhưng điều này sẽ giúp bạn bắt đầu. Hi vọng điêu nay co ich.


Cảm ơn @ CR7SMS. Nó khởi động lại tổng chạy khi loại = Hoạt động nhưng tổng chạy khi loại trống không hoạt động. Vào ngày 7 tháng 11, nó giảm xuống còn 3 nhưng sau đó từ 8-14 tháng 11, nó trả về -2. Bạn có thể giúp sửa đổi mã để tổng số hoạt động khi loại trống không? Cảm ơn
LynseyC

Xin chào Lynsey, tôi đã thử một tính toán khác. Tôi đã thêm nó như một câu trả lời khác vì tính toán hơi dài. Nhưng hy vọng tính toán mới hoạt động.
CR7SMS

@ CR7SMS vui lòng tránh thêm nhiều hơn một câu trả lời cho một câu hỏi. Nó nhầm lẫn những người dùng khác có thể tìm kiếm một vấn đề / giải pháp tương tự và nó không hay. Thay vào đó, bạn nên thêm bất cứ điều gì bạn có thể đưa ra như một giải pháp cho một câu trả lời và phân chia mọi khía cạnh khác nhau cho các phần.
Christos Lytras

2

Việc tính toán hơi dài, nhưng dường như nó đang hoạt động trong dữ liệu mẫu tôi đang sử dụng. Hãy thử xem:

Running Total = 
    VAR Employee = Leave[Employee ID]
    VAR Date1 = Leave[Date]
    VAR Prev_Blank = CALCULATE(MAX(Leave[Date]),
                        FILTER(Leave,Leave[Date] < Date1),
                        FILTER(Leave,Leave[Employee ID]=Employee),
                        FILTER(Leave,Leave[Type]=BLANK()))  
    VAR Prev_Working = CALCULATE(MAX(Leave[Date]),
                        FILTER(Leave,Leave[Date] < Date1),
                        FILTER(Leave,Leave[Employee ID]=Employee),
                        FILTER(Leave,Leave[Type]="Working"))    
    VAR Prev_Blank1 = CALCULATE(MAX(Leave[Date]),
                        FILTER(Leave,Leave[Date] < Prev_Working),
                        FILTER(Leave,Leave[Employee ID]=Employee),
                        FILTER(Leave,Leave[Type]=BLANK()))  
    VAR Prev_type = CALCULATE(MAX(Leave[Type]),
                        FILTER(Leave,Leave[Date] = Date1-1),
                        FILTER(Leave,Leave[Employee ID]=Employee))
    VAR Prev_Blank2 = IF(Leave[Type]="Working" && (Prev_Blank1=BLANK() || Prev_type=BLANK()),Date1-1,Prev_Blank1)    
    VAR Day_count_Working = CALCULATE(COUNT(Leave[Date]),
                        FILTER(Leave,Leave[Date] > Prev_Blank2),
                        FILTER(Leave,Leave[Date] <= Date1),
                        FILTER(Leave,Leave[Employee ID]=Employee),
                        FILTER(Leave,Leave[Type]="Working")) 
    VAR Day_count = CALCULATE(COUNT(Leave[Date]),
                        FILTER(Leave,Leave[Date] >= Prev_Blank2),
                        FILTER(Leave,Leave[Date] <= Date1),
                        FILTER(Leave,Leave[Employee ID]=Employee)) 
RETURN (IF(Day_count_Working=BLANK(),Day_count,Day_count-1)-Day_count_Working)*-1 + Day_count_Working

Tôi đã sử dụng một loạt các biến ở đây. Bạn có thể có thể đưa ra một phiên bản ngắn hơn. Về cơ bản, ý tưởng là tìm sự xuất hiện đầu tiên trước đó của "Làm việc" để tìm nơi bắt đầu tính toán. Điều này được tính trong biến "Prev_Blank2". Khi chúng tôi biết điểm bắt đầu (bắt đầu bằng 1 tại đây), sau đó chúng tôi chỉ cần đếm số ngày với "Làm việc" hoặc để trống () ở giữa Prev_Blank2 và ngày của bản ghi hiện tại. Sử dụng những ngày này, chúng tôi có thể trả về giá trị cuối cùng cho tổng số chạy.

Hy vọng rằng điều này không có mẹo;)

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.