#2 Jeden wykres - dwa "identyczne" slicery


#2 Jeden wykres - dwa


Zainspirowany ciekawych zadaniem, z którymi się spotkałem, pokażę Wam ciekawy sposób na to, aby jeden wykres bazujący na jednej tabeli mógł być filtrowany dwoma slicerami odnoszącymi się do tego samego artefaktu.

Już tłumaczę, o co chodzi, bo na pierwszy rzut oka może to wydawać się niejasne. Załóżmy, że mamy wykres liniowy pokazujący na przestrzeni czasu wyniki jednej prostej miary Sales SUM. To nasz punkt wyjścia.

Jednak odbiorcy raportu potrzebują wizualizacji, która pozwoli im porównywać wyniki sprzedaży pomiędzy produktami. W teorii wydaje się to proste. Wystarczy przecież dodać w opcjach formatowania wykresu do pola „Legenda” kolumnę przechowującą nazwę produktu (ProductName). Uzyskamy wtedy rozbicie na wiele osi zgodnych z elementami w legendzie. dodajmy jeszcze slicer i mamy to. Jest jedno "ale".

Wynik może nie spełniać oczekiwań – zamiast dwóch linii może pojawić się ich więcej, w zależności od liczby unikalnych produktów. Gdy produktów będzie sporo (a zwykle tak jest w raportach), to wykres stanie się nieczytelny. Przejdźmy do kolejnych akapitów, w których pokażę Ci, jak wycisnąć z Power BI nieco więcej.

Problem z filtrowaniem

Nasz efekt końcowy, którego oczekujemy od tej wizualizacji, to możliwość wyboru dokładnie dwóch produktów, nie dowolnej liczby, nie jednego, ani nie wszystkich. Dokładnie dwóch. Intuicja może podpowiadać, że przecież jest coś takiego jak opcja "single select" w ustawieniach slicera, więc włączmy ją, a następnie zduplikujmy ten slicer. Wydaje się logiczne prawda? Będziemy mieć dwa slicery, gdzie każdy będzie służył do selekcji jednego produktu. Jednak Power BI tak nie działa.

Mamy dwa slicery, których źródłem jest taka sama kolumna, dlatego powstaje konflikt. Każdy slicer zawęża listę elementów dostępnych do wyboru w kolejnych slicerach (trochę jak INNER JOIN w SQL). Dla przykładu jeśli mamy tabelę Geography i utworzymy dwa slicery - jeden odwołuje się do kolumny Country, a drugi Continent, to gdy na slicerze z kontynentami wybierzemy "Europe",  slicer z listą krajów zawęzi się do krajów z Europy. 

Chyba, że wyłączylibyśmy interakcje pomiędzy slicerami, ale to nie naprawi nam wykresu, on też wykonuje się w pewnym kontekście. Power BI działa jak policjant na przesłuchaniu. Najpierw sprawdza co "mówi" jeden slicer, następnie prześwietla drugi slicer. Jeśli "zeznania" się ze sobą nie pokrywają, już wie, że coś jest nie tak. A jeśli dla Power BI coś jest nie tak, to zamiast obliczeń zwróci BLANK (czyli brak wartości), a w najgorszym przypadku błąd.

Musimy uniezależnić od siebie slicery, ale w nico inny sposób. Będziemy do tego potrzebować osobnych, niepowiązanych ze sobą i z modelem tabel - w zasadzie to będą jednokolumnowe tabele przechowujące unikatową listę wszystkich nazw produktów.

ProductSelection_1 =
FILTER (
    DISTINCT ( Product[ProductName] ),
    NOT ( ISBLANK ( Product[ProductName] ) )
)
ProductSelection_2 =
FILTER (
    DISTINCT ( Product[ProductName] ),
    NOT ( ISBLANK ( Product[ProductName] ) )
)

Tworzenie miar do zmiany kontekstu

Teraz możemy zmienić źródła naszych slicerów, gdzie każdy z nich ma odwoływać się do kolumny ProductName z innej tabeli kalkulowanej. Pamiętaj aby na tym etapie ponownie włączyć wzajemne interakcje pomiędzy slicerami - mimo, że tabele nie są ze sobą powiązane, to ta opcja będzie nam jeszcze potrzebna w kolejnych etapach. Pozostało nam w jakiś sposób przechwycić, to co się dzieje w slicerach i wykorzystać do wpłynięcia na to jak zachowuje się wykres. Jest wiele wariantów miary, która byłaby w stanie to obliczyć, ale w każdym podstawą jest funkcja CALCULATE, która służy do modyfikacji kontekstu obliczeń. Ja do pobrania aktualnie wybranego produktu ze slicera wykorzystam SELECTEDVALUE. Końcowo potrzebujemy dwóch miar DAX, po jednej dla każdej linii na wykresie.

SalesSum_Selection_1 =
VAR SalesSum =
    SUM ( ProductSales[Sales] )
RETURN
    CALCULATE (
        SalesSum,
        FILTER (
            ProductSales,
            ProductSales[ProductName] IN VALUES ( ProductSelection_1[ProductName] )
        )
    )
SalesSum_Selection_2 =
VAR SalesSum =
    SUM ( ProductSales[Sales] )
RETURN
    CALCULATE (
        SalesSum,
        FILTER (
            ProductSales,
            ProductSales[ProductName] IN VALUES ( ProductSelection_2[ProductName] )
        )
    )

Gdy mamy już utworzone miary, przechodzimy do wykresu liniowego, usuwamy pola wrzucone do legendy, wyrzucamy też poprzednią miarę wyliczającą sprzedaż, a zamiast tego wrzucamy na oś Y nasze dwie nowe miary. Pamiętaj, aby odpowiednio dostosować ich nazwy już w wykresie.

Dopracowanie: wykluczenie tych samych wyborów

Na tym etapie, można powiedzieć, że cel został osiągnięty, ale chcemy iść o krok dalej i dodatkowo usprawnić działanie przygotowanych wizualizacji. Zauważ, że teraz użytkownik może wybrać dwa takie same produkty na obydwu slicerach, niby drobiazg, ale możemy to poprawić. Wystarczy prosta miara DAX.

ProductSelection_check =
IF (
    SELECTEDVALUE ( ProductSelection_1[ProductName] )
    = SELECTEDVALUE ( ProductSelection_2[ProductName] ),
    1,
    0
)

Co tutaj się wydarzyło? Sprawdzamy jaki wybór jaki jest aktualnie wybrany produkty z jednej tabeli, następnie porównujemy do drugiej, jeśli są takie same to miara ma zwrócić 1, a jeśli nie 0. Co wykorzystamy, aby odpowiednio odfiltrowywać elementy ze slicerów. Dodaj tę miarę do filtrowania w panelu filtrów na poziomie "filters on this visual" dla każdego slicera, ograniczając listę produktów, tylko do tych, które w wyniku tej miary zwrócą 0.

Końcowo nasz wykres prezentuje się w taki sposób:

A więc mamy to! Gratulacje 😎 Dzięki temu rozwiązaniu nie powiesz już "nie da się". Tym bardziej, że powyższe rozwiązanie można dostosować jeszcze na wiele sposobów np. selekcja ostatecznie nie musi być jednokrotna, albo jeśli zamiast dwóch miar możemy stworzyć jedną, ale za to taką, która będzie obliczać wartość sprzedaży dla elementów wybranych na obydwu slicerach łącznie. Zapisz sobie ten artykuł na później i spróbuj przetestować wyklikując poszczególne kroki samodzielnie.

Jeśli masz pytania, a może znasz inne sposoby na realizację tego zadania, napisz do mnie na moim profilu na Instagramie. Do następnego! 👋


Sprawdź też inne wpisy na moim blogu: [LINK]