전체 열을 실행하는 Power BI Desktop DAX 다시 시작


9

나는 모든 사람이 연중 매일 기록을 가지고있는 테이블을 가지고 있습니다. 이 기능을 사용하여 일일 잔액 항목을 기준으로 누계를 달성했습니다.

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

그러나 Type = Working이고 Daily Balance의 누적 합계가 0보다 작고 이전 행의 Type이 Working과 같지 않으면 누적 합계가 1에서 다시 시작해야합니다. 아래는 Excel의 스크린 샷입니다. 필요한 기능 열은 내가 알아야 할 것입니다.

여기에 이미지 설명을 입력하십시오


1
Person 1 11 월 5 일 행에 테스트 데이터 유형이 비어 있다고 가정합니다. '필수 함수'가 11 월 6 일에 1 또는 2를 반환합니까?
Ryan B.

11 월 5 일은 2가 반환됩니다. 11 월 5 일은 1 (음수가 아님)이므로 "재설정"은 발생하지 않습니다. 자세한 게시물에 감사드립니다. 오늘 검토 중입니다
LynseyC

답변:


1

이는 논리가 ID 수준에 적용되어야하기 때문에 조건이있는 누적 합계 일뿐만 아니라 중첩 / 클러스터 된 합계입니다. 큰 테이블의 경우 M이 많은 RAM을 사용하지 않기 때문에 DAX보다 M이 좋습니다. (나는 그것에 대해 블로그에 올렸다 : 블로그 포스트 링크

다음 함수는 해당 논리를 현재 사례에 맞게 조정하고 ID 수준에 적용해야합니다. (필수 열 이름 : "Type", "Daily Allowance", "Adjustments")

(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


문제가 해결되었습니다. 완벽하게 작동하며 보고서 속도를 늦추지 않았습니다. 감사합니다
LynseyC

5

개요

PowerBI에 요청하는 것은 어려운 일이므로 깔끔한 접근 방식을 찾기가 어려울 수 있습니다.

가장 큰 문제는 PowerBI의 데이터 모델이 적어도 Excel에서하는 방식이 아니라 달리는 탈리 개념을 지원하지 않는다는 것입니다. Excel에서 열은 동일한 열의 '이전 행'에서 발생하는 값을 참조한 다음 다른 열에 나열된 '일별 변경'으로 조정할 수 있습니다.

PowerBI는 일부 하위 행에 대한 모든 일일 변경 사항을 추가하여이를 모방 할 수 있습니다. 현재 행의 날짜 값을 가져 와서 모든 날짜가이 현재 행의 날짜보다 작은 필터링 된 테이블을 만든 다음 해당 하위 집합의 모든 일일 변경 사항을 요약합니다. 이것은 미묘한 차이처럼 보이지만 상당히 중요합니다.

이는 누적 합계를 '재정의'할 방법이 없음을 의미합니다. 수행되는 유일한 수학은 매일의 변경 사항을 포함하는 열에서 발생합니다. '총계'가 포함 된 열은 결과 일뿐입니다. 이는 후속 행 계산에 사용되지 않습니다.

우리는 '재설정'이라는 개념을 버리고 대신 '조정'값을 포함하는 열을 만드는 것을 상상해야합니다. 조정은 설명 된 조건이 충족 될 때 일일 잔고 및 조정의 총계가 1이되도록 포함 할 수있는 값입니다.

OP가 제공 한 계산 된 실행 량을 보면, '작업 일'직전의 '비 작업'일에 실행 된 총계의 값에 따라 반대로 계산하면 0과 다음 영업일의 누적 합계가 1 씩 증가합니다. 이것은 우리가 원하는 행동입니다 (나중에 설명 할 문제가 하나 있습니다).

결과

여기에 이미지 설명을 입력하십시오

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"
))

행 컨텍스트와 필터 컨텍스트의 차이점과 EARLIER가이 계산을 수행하는 방법을 이해하는 데 도움이됩니다. 이 시나리오에서 "EARLIER"는 "이 참조는 현재 행의 값을 가리 킵니다"를 의미하고 그렇지 않으면 "ALLEXCEPT (Leave, Leave [Id])"에 의해 리턴 된 전체 테이블을 가리 킵니다. 현재 행에 "Working"유형이 있고 전날 행에 다른 유형이있는 장소를 찾습니다.

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])
))

이 계산은 '채우기'종류의 작업을 모방합니다. "이 행의 날짜 이전 날짜가있는 모든 행을 볼 때 '근무 전 가장 최근 날짜'에서 가장 큰 값을 반환합니다."

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])
))

이제 모든 행에 조정으로 사용할 일일 잔고를 어디로 찾아야하는지 설명하는 필드가 있으므로 테이블에서 찾아 볼 수 있습니다.

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

마지막으로 조정 결과를 최종 결과에 대한 누적 합계에 적용합니다.

문제

이 방법은 일일 일일 잔고가 0 미만이 아니면 카운트를 재설정해서는 안된다는 점을 해결하지 못합니다. 나는 전에 틀린 것으로 판명되었지만 순환 의존성을 생성하기 때문에 이것이 DAX에서만 달성 할 수 없다고 말할 것입니다. 기본적으로 요구 사항을 만듭니다. 집계 된 값을 사용하여 집계에 포함 할 대상을 결정하십시오.

그래서 내가 당신을 데려 올 수있는 한입니다. 도움이 되길 바랍니다.


1
당신의 마지막 요점에 관해서는, 나는 당신이 맞다고 생각합니다. DAX는 재귀를 수행 할 수 없습니다.
Alexis Olson

3

다음에 그림 대신 샘플 데이터를 생성하는 CSV 또는 코드를 붙여 넣기를 바랍니다. :)

대신 PowerQuery에서 계산을 제안하십시오. 가독성을 높이기 위해 몇 단계로 코드를 분할하려고했습니다. 좀 더 복잡해 보이지만 잘 작동합니다. 고급 편집기에 붙여 넣은 다음 소스를 소스 데이터로 바꾸십시오. 행운을 빌어 요!

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

나는 이것이 모든 시나리오를 다룰 것이라고 확신하지는 않지만 올바른 접근법처럼 보입니다.
Mike Honey

각 사람의 첫 번째 유형이 작동하는 경우에만 작동하도록 할 수 있습니다. 또한 DAX 예제와 마찬가지로 이전 행의 누적 합계가 양수인 경우 워킹 무브먼트의 번호 매기기가 다시 시작됩니다. 나는이 시나리오 만 포함되어 있기 때문에 내 사진이 오도 된 것 같습니다. 유형이 작동으로 변경되었지만 이전 행 총계가 양수 인 시간을 포함해야했습니다.
LynseyC

@LynseyC 글쎄,이 코드는 완벽하고 완벽한 솔루션은 아니지만 사용할 수있는 방법의 예입니다. 시나리오의 경우 수정하십시오.
유진

@LynseyC는 DAX 대신 PowerQuery에서이 수학을 수행 할 때의 장점 중 하나는 임시 열을 데이터 모델에서 유지하는 쉬운 방법입니다.
유진

3

나는 그것을 가지고 있다고 생각합니다!

이전에 게시 한 솔루션을 기반으로 한 결과는 다음과 같습니다. (데이터가 더 많은 "작업 / 작업 없음"동작 및 사용 사례를 표시하도록 수정되었습니다.)

결과

여기에 이미지 설명을 입력하십시오

세부

(1) "Adjusted Running Daily Balance"및 "Daily Balance Adjustment"열을 삭제하십시오. 단 한 번의 단계만으로도 동일한 결과를 얻을 수 있습니다.

(2) 다음 열을 생성합니다 (RDB = "일일 잔고 실행") ...

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]) 
))

"작업 완료 전 가장 최근 날짜"를 만들었으므로 실제로는 내가 불가능하다고 주장한 '재설정'을 수행하는 데 필요한 부분이 있습니다. 이 필드를 필터링하면 각 슬라이스를 '1'에서 시작할 수 있습니다.

(3) 우리는 여전히 똑같은 문제를 가지고 있습니다. 우리는 열의 결과를 볼 수 없으며 그것을 사용하여 나중에 같은 열에서 무엇을 해야할지 결정합니다. 그러나 우리는 그 정보를 담을 새로운 조정 칼럼을 만들 수 있습니다! 그리고 우리는 이미 '근무 전 가장 최근 날짜'에 대한 참조를 가지고 있습니다. 그것은 이전 그룹의 마지막 날입니다 ... 필요한 정보가있는 행입니다!

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)

따라서 이전 그룹 의 마지막 날을 살펴보고 해당 조정의 총합에 양수 값이 있으면이를 적용하고 음수이면 대신 그대로 둡니다. 또한 개인의 처음 며칠이 휴무일 인 경우 조정시 초기 음수 비트를 원하지 않으므로 필터링됩니다.

(4)이 마지막 단계는 조정을 최종 결과로 가져옵니다. 두 개의 새로운 열을 요약하면 마지막으로 조정 된 일일 일일 잔고가 있어야합니다. 짜잔!

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

우리는이 결과로가는 길에 추가 열을 많이 만들었습니다.이 열은 일반적으로 내가 좋아하는 일이 아닙니다. 그러나 이것은 까다로운 일이었습니다.


안녕 @ 라이언 B. 이것은 내 조직의 200 명 이상을 위해 완벽하게 작동하지만 작동하지 않습니다. 코드를 직접 변경하려고 시도했지만 문제를 해결할 수있는 방법이 없습니다. 오랜 시간 근무한 후 하루 만 더 쉬었 기 때문이라고 생각합니다. 문제를 보여주기 위해 이미지에 연결했습니다. 감사 이미지
LynseyC

"그룹화 된 RDB 조정"측정 값을 수정하여 여러 "작업 / 작업 없음"주기에 걸쳐 많은 휴가가 발생하도록했습니다.
Ryan B.

2
모든 노력에 감사드립니다. 불행히도 수정으로 문제가 해결되지 않았습니다. 그러나 "Leave [최근 최근 작업 일자 완료] <> Blank ()"필터에서 마지막 조건을 제거하면 문제가 해결되었지만 원래 사람들의
종아리를

사격. 글쎄, 나는 당신이 작동하는 것을 찾을 수 있기를 바랍니다.
Ryan B.

2

시간이 걸렸지 만 해결 방법을 생각해 냈습니다. 블랭크의 잔액 값은 항상 -1이고 "작업 중"의 값은 1이고 데이터는 간격없이 모든 날짜에 사용할 수 있다고 가정하면 아래 계산과 같이 작동합니다.

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

작은 샘플로 작업했을 때이 제품은 완제품이 아닐 수도 있지만 시작해야합니다. 도움이 되었기를 바랍니다.


@ CR7SMS에게 감사합니다. 유형 = 작동 중일 때 누계를 다시 시작하지만 유형이 비어있을 때 누계가 작동하지 않습니다. 11 월 7 일의 경우 3으로 감소하지만 11 월 8 일에서 14 일의 경우 -2를 반환합니다. 유형이 비어있을 때 누적 합계가 작동하도록 코드를 수정하는 데 도움을 줄 수 있습니까? 감사합니다
LynseyC

안녕 Lynsey, 나는 다른 계산을 시도했다. 계산이 약간 길어서 다른 답변으로 추가했습니다. 그러나 새로운 계산이 작동하기를 바랍니다.
CR7SMS

@ CR7SMS 단일 질문에 둘 이상의 답변을 추가하지 마십시오. 비슷한 문제 / 솔루션을 검색 할 수있는 다른 사용자를 혼란스럽게하며 좋지 않습니다. 대신 하나의 답변에 대한 해결책으로 나타날 수있는 모든 것을 추가하고 각기 다른 측면을 섹션으로 분할해야합니다.
Christos Lytras

2

계산이 약간 길지만 사용중인 샘플 데이터에서 작동하는 것 같습니다. 이것을 시도하십시오 :

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

나는 여기에 많은 변수를 사용했습니다. 더 짧은 버전을 만들 수도 있습니다. 기본적으로 아이디어는 계산을 시작할 위치를 찾기 위해 이전에 발생한 "Working"을 찾는 것입니다. 이는 "Prev_Blank2"변수에서 계산됩니다. 시작점을 알고 나면 (여기서 1로 시작) Prev_Blank2와 현재 레코드 날짜 사이에 "Working"또는 blank ()를 사용하여 일 수를 계산할 수 있습니다. 요즘에는 누적 합계의 최종 값을 반환 할 수 있습니다.

희망적으로 이것은 트릭을 수행합니다.)

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.