---
title: "Statystyczna kuchnia: Kaiserschmarrn"
description: ""
date: 2023-08-05
featured_image: "/post/y2023/statystycznakuchnia/IMG_20230730_130344964_PORTRAITc-1.jpg"
tags: [ "kaiserschmarrn", "statystyczna kuchnia", "pca", "umap", "klastrowanie" ]
output: hugodown::md_document
categories: [ "Statystyka" ]
execute:
  warning: false
  echo: false
knitr:
  opts_chunk:
    fig-path: "statystycznakuchnia"
---

```{r,echo=FALSE}
## Set default options for the knitr RMD processing
knitr::opts_chunk$set(echo=FALSE,
  warning=FALSE,
  message=FALSE,
  fig.width=10,
  fig.height=5,
  fig.path="",
  cache=FALSE,
  autodep=TRUE, 
  results="hide")
```

Dzisiaj opowiem Wam, jak zrobić austriacki Kaiserschmarrn z jajek, mąki, wody i
statystyki.
<!--more-->

[Przepis na dole.](#przepis)
(Wiem, też nie znoszę tych blogów, w których najpierw ktoś opowiada
swoją historię życia, a na samym końcu umieszcza przepis. Na swoje usprawiedliwienie
mam to, że to nie jest blog poświęcony gotowaniu.)

---

Kaiserschmarrn albo Kaiserschmarren to popularna w krajach niemieckojęzycznych potrawa – w smaku
dość zbliżona do amerykańskich pancakes, wywodząca się z Austrii. Jest
[kilka legend](https://info.bml.gv.at/themen/lebensmittel/trad-lebensmittel/speisen/kaiserschmarren.html)
łączących nazwę z kajzerem Franciszkiem Józefem I. 

*Schmarrn* to ogólne określenie chłopskiej potrawy z jajek, masła, mleka i
mąki. Jedna z legend głosi, że Franciszkowi Józefowi podano Schmarrn podczas jednego z polowań w
[Salzkammergut](https://de.wikipedia.org/wiki/Salzkammergut); ale na cześć
cesarza dodano doń rodzynek w rumie i posypano cukrem pudrem. Etymolodzy
podejrzewają jednak, że część *Kaiser* w rzeczywistości pochodzi od *Kaser*
(= *Senner*, pasterz w dialekcie tyrolskim) albo *Casa* (dom).

Bardzo dobry jest Kaiserschmarrn, nawet bez rodzynek (nigdy go nie robię z
rodzynkami, bo niektórzy nie lubią), czy to jako deser, czy jako śniadanie.
Można dodać (zgodnie z tradycją) musu z jabłek, albo (totalnie wbrew
tradycji) polać syropem klonowym. Często robiłem go w niedzielę jak moi
synowie byli jeszcze mali. 

Oprócz jedzenia, Kaiserschmarrn świetnie nadaje się do nauki statystyki. Bo
widzicie, już dosyć dawno zacząłem stosować quasi-statystyczną metodologię
przy gotowaniu. Kiedy zabieram się za jakiś nowy przepis, zwłaszcza jeśli
jest to jakaś popularna potrawa, to zamiast skorzystać z jednego
konkretnego wariantu czytam kilka czy nawet kilkanaście. Dzięki temu
nabieram wyczucia: co jest istotne, a co nie, jaki jest zakres zmienności
składników, jakie są różnice w sposobie przygotowania, itd. Na dłuższą metę
okazuje się to bardzo skuteczne.

Nie uwierzycie, ale zebrałem [100 różnych przepisów](kaiserschmarrn.csv) na
Kaiserschmarrn, i jeszcze dodałem swój własny oraz trzy wygenerowane przez
AI. Teraz je sobie przeanalizujemy[^1].

[^1]: Kod analizy, czyli całą tę notkę w formacie quarto (wariant
rmarkdown)
znajdziecie [tutaj](statystycznakuchnia.qmd).

---


```{r libraries}
## to my, trociny
library(tidyverse)
library(ggplot2)
library(ggbeeswarm)
library(cowplot)
library(ggrepel)
theme_set(theme_minimal())
```

```{r read_data}
ks <- read_csv("kaiserschmarrn.csv") %>%
  mutate(proszek = proszek > 0) %>%
  mutate(AI = ifelse(grepl("(chatGPT|Google Bard|copilot|moje)", url),
                     url, NA))
```

Zanim zabierzemy się za analizę, trzeba by te dane jakoś
znormalizować. No bo wiadomo: jeśli wezmę dwa jajka, 100 gr mąki i 200 g
mleka to dostanę właściwie to samo, co jakbym wziął jedno jajko, 50 g mąki
i 100 g mleka, tylko dwa razy więcej. Znormalizujemy więc najpierw dane
tak, żeby przeliczyć procentowy udział składników według wagi (liczę przy
tym, że standardowe jajko z Sevres ma 55g, oraz przyjmuję gęstość mleka
1g/ml).

```{r normalize}
## calculate g/kg
ks2 <- ks %>% 
  mutate(jajka = jajka * 55) %>%
  rowwise() %>%
  mutate(waga = sum(c_across(jajka:cukier))) %>% 
  ungroup() %>%
  mutate(across(jajka:cukier, 
                ~ .x / waga * 100, 
                .names = "{.col} (%)")) %>%
  mutate(woda_proc = `jajka (%)` * .75 + `mleko (%)` * .9 + `woda mineralna (%)`)

## long data format for ggplot
ks_l <- ks2 %>% pivot_longer(cols = c(ends_with("(%)"), woda_proc), names_to = "składnik", values_to = "g") %>% 
  filter(!is.na(g)) %>% 
  filter(g > 0) %>%
  mutate(składnik = str_replace(składnik, " \\(%\\)", "")) %>%
  mutate(składnik = gsub("woda_proc", "woda całkowita", składnik)) %>%
  mutate(składnik = factor(składnik, levels = c("mąka", "mleko", "woda mineralna", "cukier", "jajka", "woda całkowita")))
```


Pierwsze, co się rzuca w oczy, to olbrzymia różnorodność receptur.
Oczywiście, wszystkie przepisy zawierają jajka, mąkę, mleko i cukier, a
znakomita większość z nich również cukier waniliowy.

Ale proporcje jajek, mąki i mleka są bardzo różne. Poza tym, niektóre
przepisy każą dodawać proszek do pieczenia, niektóre wodę mineralną.
Popatrzmy na procentowy skład masy:


```{r simple_viz,fig.width=6,fig.height=4}
ggplot(ks_l, aes(x=składnik, y=g)) + 
  geom_boxplot(outlier.shape=NA) + 
  geom_beeswarm(alpha=.3) +
  labs(x = "Składnik", y = "% masy")
```


Jeden z przepisów w 20% składa się z cukru!
Waha się nawet gęstość masy, wyrażona przez procentowy udział wody
(kategoria `woda całkowita`), między 45% a ponad 70%.


```{r calc_average}
# calculate for 2 eggs (110 g)
means <- ks_l %>% 
  filter(składnik != "woda całkowita") %>%
  group_by(składnik) %>% 
  summarise(mean = mean(g)) %>%
  ungroup() %>%
  mutate(mean = 100 * mean / sum(mean))
means_2j <- set_names(means$mean, means$składnik)
means_2j <- means_2j / means_2j["jajka"] * 110

## add the mean recipe to the data

.df <- means_2j %>%
  as.data.frame() %>%
  rownames_to_column("składnik") %>%
  set_names("składnik", "mean") %>%
  pivot_wider(names_from = składnik, values_from = mean) %>%
  cbind( means %>% 
    mutate(składnik = paste(składnik, "(%)")) %>%
    pivot_wider(names_from = składnik, values_from = mean)
  ) %>%
  mutate(url="uśrednione", AI="uśrednione") %>%
  mutate(woda_proc = `jajka (%)` * .75 + `mleko (%)` * .9 + `woda mineralna (%)`)
ks2 <- ks2 %>% add_row(.df)
```

Może powinniśmy po prostu wszystko uśrednić? Wziąć, dajmy na to, 2 jajka, 
`r round(means_2j["mąka"] / 10, 0) * 10` g mąki, 
`r round(means_2j["mleko"] / 10, 0) * 10` ml mleka,
`r round(means_2j["woda mineralna"] / 10, 0) * 10` ml wody mineralnej i
`r round(means_2j["cukier"] / 10, 0) * 10` g cukru? Prawdopodobnie
zadziałałoby całkiem nieźle, ale to by było za proste. Po co prosto, jeśli
można się tymi danymi pobawić?

---

Pewien bioinformatyk powiedział mi kiedyś, że wszyscy mają jakąś swoją
ulubioną metodę analizy, którą stosują w pierwszej kolejności – nieważne, jakie są
dane. Moja ulubiona metoda to analiza komponentów głównych (principal
component analysis, PCA). Ma oczywiście wiele ograniczeń, ale jak dla mnie
olbrzymią zaletę, jaką jest jej prostota i łatwość w interpretacji.

Poniżej wynik PCA:


```{r pca}
## skalujemy explicite 
mtx <- ks2 %>% select(ends_with("(%)")) %>% as.matrix()
mtx_norm <- scale(mtx)
pca <- prcomp(mtx_norm, scale.=FALSE)
df <- ks2 %>% cbind(pca$x)
```


```{r pca_fig,fig.width=8,fig.height=4}
p1 <- ggplot(df, aes(x = PC1, y = PC2, color = AI, label = AI)) + geom_point() + 
  labs(x = "PC1", y = "PC2") +
  geom_label_repel() +
  theme(legend.position = "none")
p2 <- ggplot(df, aes(x = PC3, y = PC4, color = AI, label = AI)) + geom_point() + 
  labs(x = "PC3", y = "PC4") + 
  geom_label_repel() +
  theme(legend.position = "none")
plot_grid(p1, p2, ncol = 2)
```

Powyżej widzimy cztery pierwsze komponenty. Kolorowo oznaczone są 
przepisy, które zostały wygenerowane przez sztuczną inteligencję, mój
przepis i przepis uśredniony. Jak widać, OpenAI jest zachowawczy i
przepisami trafia w centrum. Natomiast Google Bard... um, ma najwyraźniej
dosyć odjechany przepis. Nic dziwnego: na dwa jajka, bard zaproponował
prawie pół litra płynu - to ponad 65% całej masy. Gwarantuję wam, że z tego
nie wyjdzie nic ciekawego.

No dobrze, ale co właściwie mówią nam komponenty?

Każda komponenta to jest [kombinacja liniowa](https://pl.wikipedia.org/wiki/Kombinacja_liniowa) 
użytych składników. Innymi słowy, pozycja danego przepisu na osi oznaczonej
"PC1" zależy od tego, ile w przepisie jest mąki, ile mleka, ile wody i ile
cukru. Ale nie jest to zwykła suma, tylko suma z wagami: niektóre składniki
mają większy wpływ na pozycję przepisu na osi, a niektóre mniejszy.

Na przykład, na osi PC1 przepisy ułożone bardziej na prawo mają więcej mąki
i mleka, a mniej jajek i cukru. Natomiast na osi PC2 przepisy ułożone wyżej
mają więcej mleka i jajek, a mniej mąki i wody mineralnej[^2].

[^2]: Przed policzeniem komponentów zmienne zostały wyskalowane, tak żeby
średnia każdej z nich była równa 0, a odchylenie standardowe równe 1.


```{r pca_loadings,fig.width=10, fig.height=12}
p1 <- ggplot(df, aes(x = PC1, y = PC2, color = `mleko (%)` + `mąka (%)`)) + 
  geom_point(size=5)
p2 <- ggplot(df, aes(x = PC1, y = PC2, color = `mąka (%)` + `woda mineralna (%)`)) + 
  geom_point(size=5)
#p1 <- ggplot(df, aes(x = PC1, y = PC2, color = proszek)) + geom_point()
#p2 <- ggplot(df, aes(x = PC1, y = PC2, color = proszek)) + geom_point()
plot_grid(p1, p2, ncol = 1)
```

Pytanie, jakie mnie jednak nurtowało, brzmi inaczej: czy istnieją pewne
*grupy* przepisów? Like, czy można podzielić przepisy na np. bardzo
jajeczne i mało jajeczne, albo na ciężkie i lekkie? Liczyłem na to, że
dostrzegę te grupy już na wykresie PCA, ale niestety nie udało się i przyjdzie nam
wytoczyć cięższe działa.

---

W mojej dziedzinie
bardzo popularną alternatywą do PCA jest
[umap](https://umap-learn.readthedocs.io/en/latest/how_umap_works.html).
Podobnie jak PCA, umap jest metodą redukcji wymiarowości, ale dużo bardziej
skomplikowaną. Jeśli Was interesuje jak działa umap, to
[tu jest opis](https://umap-learn.readthedocs.io/en/latest/how_umap_works.html), 
a [tu](https://pair-code.github.io/understanding-umap/) znajdziecie świetne
demonstracje (polecam zwłaszcza zabawę mamutem).

Zaletą umapa jest jego zdolność do wykrywania grup, nawet
wtedy, gdy grupy te nie są wyraźnie rozdzielone. W przeciwieństwie do PCA,
Umap nie jest liniowy, co oznacza, że nie można go opisać jako kombinacji
liniowej zmiennych wejściowych – i to jest jego wada, bo nie mogę łatwo
powiedzieć, skąd się te nowe współrzędne biorą.

```{r umap,fig.width=5,fig.height=4}
library(umap)
set.seed(123)
conf <- umap.defaults
conf$metric <- "pearson"
conf$n_epchs <- 5000
conf$n_neighbors <- 15
conf$min_dist <- .02
umap <- umap(mtx_norm, conf)$layout %>% as.data.frame() %>% setNames(c("UMAP1", "UMAP2"))
df2 <- df %>% cbind(umap)
ggplot(df2, aes(x = UMAP1, y = UMAP2, color = AI, label = AI)) + geom_point() + 
  geom_label_repel() +
  theme(legend.position = "none")
```

To już jest ciekawe! W tej projekcji przepisy są podzielone na
przynajmniej trzy, jeśli nie cztery grupy; np. w jednej jest Google Bard, 
w drugiej chatGPT 3.5, w trzeciej – chatGPT 4.0.

Rzecz ciekawa: proporcje uśrednione grupują się razem z przedziwnym 
przepisem barda. Dlaczego? Prawdopodobnie dlatego, że proporcje uśrednione
zawierają też niewielki dodatek wody. Najwyraźniej umap zgrupował wszystkie
przepisy, które dodają wodę mineralną do ciasta. Łatwo to sprawdzić: na obrazku
poniżej kolory oznaczają, czy przepis dodaje wodę, czy nie.



```{r umap_woda_mineralna,fig.width=7,fig.height=4}
ggplot(df2, aes(x = UMAP1, y = UMAP2, color = `woda mineralna (%)` > 0, label = AI)) + 
  geom_point() + 
  scale_color_brewer(palette = "Set1") +
  geom_label_repel(show.legend = FALSE)
```




Zanim zabierzemy się za sprawdzanie, czym oprócz wody te grupy się
różnią, spróbujmy sprawdzić, ile w ogóle tych grup (klastrów) jest.

---

Poniżej do grupowania skorzystam z algorytmu k-średnich (ang. k-means),
bardzo prościutkiego i bardzo popularnego. Metoda wymaga jednak wstępnego
podania liczby grup, na które chcemy podzielić dane, a przecież dokładnie
to chcielibyśmy wiedzieć. Dlatego zastosujemy prostego tricka, którego ja
nazywam "krzywą łokciową" (w ang. stosuje się określenie "elbow method").
Wygląda to tak: używamy algorytmu, żeby podzielić dane na jeden, dwa, trzy,
cztery, itd. grupy. Dla każdego podziału liczymy sobie pewną wartość
(co to konkretnie jest, opowiem za moment), która
nam mówi, jak ciasno przepisy się grupują, i narysujemy sobie wynik
zależnie od liczby grup.


```{r łokieć,fig.width=5,fig.height=4}
nclust <- 2:10
clsts <- lapply(nclust, function(k) {
  kmeans(umap, centers = k, nstart = 25)
})
frac <- sapply(clsts, function(clst) {
  clst$tot.withinss / (clst$betweenss + clst$tot.withinss)
})

aic <- sapply(clsts, function(clst) {
  clst$tot.withinss + 2 * nrow(clst$centers) * ncol(clst$centers)
})

p <- ggplot(data.frame(n=nclust, frac=frac), aes(x=n, y=frac)) + geom_point() + geom_line() +
  ylab("Część zmienności wewnątrz grupy") + xlab("Liczba grup")
print(p)
```

Na obrazku powyżej mamy jakby rękę: u góry po lewej jest ramię, u dołu po
prawej jest dłoń, a gdzieś pośrodku jest łokieć. Ten łokieć nas interesuje:


```{r łokieć_zarys,fig.width=5,fig.height=4}
ggdraw(p) + draw_image("łokieć.png", hjust=-.05, scale=.9, vjust=.1)
```


OK, ale żeby dokładnie wyjaśnić co właściwie jest ta wartość na osi Y,
musimy pogadać o zmienności. 

---

Przepisy na Kaiserschmarrn różnią się od siebie, zatem mają pewną
zmienność. Zmienność w statystyce ma pewną ciekawą właściwość: jest
addytywna. Jeśli na przykład mamy dwa zestawy danych, i każdy z własną
zmiennością, to zmienność całego zestawu danych jest równa sumie
zmienności obu zestawów *plus* zmienności między tymi zestawami.

Ja wiem, ciężko sobie wyobrazić o czym ja właściwie mówię, w każdym razie
bez dokładnego zdefiniowania zmienności i podania paru wzorów. Może ten
obrazek trochę pomoże:

![zmienność](zmienność.png)

Całkowitą zmienność naszych przepisów na Kaiserschmarrn możemy
podzielić na część *między grupami* i *wewnątrz grup*.
Innymi słowy, jeśli podzielimy dane na dajmy na to trzy grupy, to całkowita
zmienność wszystkich danych jest równa zmienności *wewnątrz tych trzech
grup* plus zmienności *między grupami*.


Wyobraźcie sobie, że w ogóle nie ma podziału na grupy. 
W takim razie, nie ma żadnej zmienności między grupami, prawda?
Czyli cała zmienność równa jest zmienności w grupach, bo cały zestaw
przepisów to jedna wielka grupa. 

A teraz drugie ekstremum: dzielimy przepisy na tyle grup, ile jest
przepisów. Czyli w każdej grupie znajdzie się dokładnie jeden przepis. W
takim wypadku nie ma zmienności *wewnątrz* grup – bo wewnątrz grup jest
zawsze tylko jeden stały punkt. *Cała* zmienność jest między grupami.

Oczywiście, oba te ekstrema są mało ciekawe. Nas interesuje jakiś złoty
środek. 

---

Obrazek z łokciem pokazuje na osi Y jaka jest część zmienności *wewnątrz*
grup. Zauważcie, że im więcej mamy grup, tym mniejsza jest część
zmienności wewnątrz grup. Ale nie tylko to: spadek zmienności też maleje.
Dla dwóch grup, prawie połowa zmienności jest wewnątrz grup. Dla trzech,
mniej niż jedna czwarta (spadek o 25). Ale dla czterech – jedna dziesiąta
(spadek o 15). I tak dalej. W pewnym momencie robi się to mało opłacalne:
mnożymy grupy bez potrzeby, ale nie zmieniamy dramatycznie sytuacji.

Oczywiście, że z jednej strony chcielibyśmy mieć grupy, które się od siebie
różnią – a więc maksymalizować zmienność między grupami. Z drugiej jednak
strony, nie chcemy mieć tych grup za dużo.

Prosta metoda "na oko" wygląda tak: wybieramy tę liczbę grup, gdzie
znajduje się "łokieć". Metoda jest co prawda bardzo niedokładna (i zależy
od tego, jak zmrużymy oczy), ale w praktyce sprawdza się całkiem nieźle. W
naszym przypadku, wygląda mi na to, że najlepszy podział jest na cztery
grupy[^3].

[^3]: Oczywiście, istnieją znacznie bardziej "fachowe" metody. Analiza
sylwetek sugerowałaby, że mamy raczej do czynienia z pięcioma grupami,
natomiast statystyka GAP potwierdza, że mamy cztery grupy.

To teraz zajmiemy się tymi czterema grupami.

---

Najpierw rysunek pokazujący jak właściwie wyglądają te grupy:


```{r umap_grupy,fig.width=5,fig.height=4}
set.seed(123)
clusts <- kmeans(umap, centers = 4)
df3 <- df2 %>% mutate(Grupa = factor(paste0("C", clusts$cluster)))
ggplot(df3, aes(x=UMAP1, y=UMAP2, color=Grupa, label=AI)) + 
  geom_point() + 
  geom_label_repel(show.legend=FALSE) 
  #theme(legend.position = "none")
```

To bardzo fajnie, ale nie mówi nam to wiele o tym, co te grupy oznaczają.
Ale akurat w naszym przypadku to jest łatwe, bo nie mamy zbyt wielu
zmiennych. Po prostu zobaczymy, jaki jest procentowy udział poszczególnych
składników w każdej grupie.


```{r składniki,fig.width=8,fig.height=6}
df3_l <- df3 %>% 
  arrange(Grupa, `woda mineralna (%)`, `mleko (%)`, `mąka (%)`) %>%
  mutate(url = factor(url, levels=unique(url))) %>%
  pivot_longer(c(ends_with(" (%)"), woda_proc), names_to="składnik", values_to="%") %>%
  mutate(składnik = gsub("woda_proc", "woda całkowita", składnik))

ggplot(df3_l, aes(x=Grupa, y=`%`)) + 
  ylab("") +
  geom_boxplot() + 
  facet_wrap(~składnik, scales="free_y")
```

Możemy też użyć nieco innej wizualizacji. Poniższe słupki pokazują
medianę procentowego udziału poszczególnych składników w każdej grupie. 
Wąsy błędów (jak po polsku jest "error bar"?) pokazują zaś kwartyle 1 i 3. Przy
okazji: jest to jeden z nielicznych uzasadnionych przypadków użycia słupków
w wizualizacji danych, bo pokazujemy procenty. Ładne, bo kolorowe, ale
widać, że poprzednia wizualizacja lepiej umożliwia między grupami.

```{r składniki_barplot,fig.width=8,fig.height=4}
df3_l2 <- df3_l %>% 
  group_by(Grupa, składnik) %>%
  summarise(
            Q1=quantile(`%`, 0.25),
            Q3=quantile(`%`, 0.75),
            `%` = median(`%`))
ggplot(df3_l2, aes(x=Grupa, y=`%`, fill=składnik)) + 
  ylab("") +
  geom_bar(stat="identity", position=position_dodge()) +
  geom_errorbar(aes(ymin=Q1, ymax=Q3), width=.2, position=position_dodge(.9)) +
  scale_fill_brewer(palette="Set1")
```





No i pięknie:

  * grupa 1: mało cukru, sporo mleka
  * grupa 2: dużo cukru, gęste ciasto
  * grupa 3: dodatek z wody mineralnej (Google Bard), mniej mleka i jajek
  * grupa 4: dużo mleka, dużo mąki (tu jest mój przepis)

I jeszcze może jedna informacja. Łatwo policzyć, który przepis ma ile
kalorii na 100g.


```{r kcal,fig.width=5,fig.height=4}
df3 <- df3 %>%
  mutate(kcal = `mąka (%)` * 364 + `mleko (%)` * 60 + 
  `jajka (%)` * 155 + `cukier (%)` * 400) %>%
  mutate(kcal = kcal / 100)
ggplot(df3, aes(x=Grupa, y=`kcal`)) + geom_boxplot()
```

A teraz wreszcie kolej na mój przepis.

---

## Przepis

### Składniki

Ilość na dwoje dorosłych albo jednego nastolatka. Wliczając masło i cukier
puder, ca. 200 kcal/100g, czyli jakieś 1000 kcal na całość 
(w obliczeniach powyżej wychodziło 
`r df3 %>% filter(url == "moje") %>% pull(kcal) %>% round(0)` kcal/100g, ale
to nie uwzględniało dodatków).

 * 2 jajka, rozdzielone na żółtka i białka
 * 100 g mąki
 * 200 g mleka albo 100 g jogurtu naturalnego + 100 g wody
 * 30 g cukru
 * szczypta soli
 * paczka cukru waniliowego
 * pół paczki proszku do pieczenia (niekoniecznie)
 * cukier puder do posypania
 * masło do smażenia (dobra czubata łyżka na 2 jajka)

### Przygotowanie

Robię w trzech naczyniach:
 
 * białka ubijam mikserem na twardą pianę w jednym naczyniu
 * potem w drugim mieszam mąkę i mleko
 * a na końcu ucieram w trzecim żółtka z solą, *połową* cukru i cukrem waniliowym (i proszkiem do pieczenia, jeśli dodaję)

Żółtka muszą być z cukrem fest utarte, aż kolor zmieni się z żółtego na
kremowy. Kolejność jest dyktowana użyciem miksera: nie chcę myć go po
każdym użyciu, a w ten sposób nie muszę.

Jak ktoś lubi rodzynki, to to jest właściwy moment żeby je dodać. Można je
też uprzednio namoczyć w rumie (ale kto wieczorem zastanawia się, czy
następnego dnia będzie robił Kaiserschmarrn?).

Potem najpierw mieszam żółtka i mąkę (mikserem), a następnie dodaję dużą
łychą białka i delikatnie je rozprowadzam.

Patelnię rozgrzewam, ale bardzo delikatnie - na niewielkim ogniu. Roztapiam
masło i wylewam masę (masa z dwóch jajek akurat pasuje na patelnię 26cm).
Grubość warstwy powinna być 1-2.5 cm. Trzeba teraz masę delikatnie smażyć
na małym ogniu aż spód się zrumieni i masa trochę stężeje. Jak jest za duży
ogień, to spód przypali się zanim masa stężeje (been there, done that).
Za to jeśli masa nie całkiem stężeje, to nie ma tragedii.

![](IMG_20230730_125007302.jpg)

Następnie należy masę przewrócić na drugą stronę. Ja to robię dwiema dużymi
łopatkami, ale można też użyć talerza. Jeśli się rozwali, nie szkodzi!
Kaiserschmarrn i tak podaje się w kawałkach.

![](IMG_20230730_125619503b.jpg)

Teraz najpierw chwilę rumienię Kaiserschmarrn z drugiej strony, a potem
rozrywam na kawałki (tak długości / grubości kciuka mniej więcej) i
posypuję resztą cukru. Zwiększam ogień, żeby kawałki ładnie się zbrązowiły,
a cukier trochę skarmelizował. 

![](IMG_20230730_130220444b-1.jpg)

Gotowe, jeszcze tylko posypać cukrem pudrem.

![](IMG_20230730_130344964_PORTRAITb-1.jpg)

---

<p>&nbsp;</p>
