R Language
Czynniki
Szukaj…
Składnia
- współczynnik (x = znak (), poziomy, etykiety = poziomy, wykluczenie = NA, uporządkowane = is.ordered (x), nmax = NA)
- Run
?factor
lub zapoznać się z dokumentacją w Internecie.
Uwagi
Obiekt ze factor
klasy jest wektorem o określonym zestawie cech.
- Jest przechowywany wewnętrznie jako wektor
integer
. - Utrzymuje atrybut
levels
pokazuje reprezentację znaków dla wartości. - Jego klasa jest przechowywana jako
factor
Aby to zilustrować, wygenerujmy wektor 1000 obserwacji z zestawu kolorów.
set.seed(1)
Color <- sample(x = c("Red", "Blue", "Green", "Yellow"),
size = 1000,
replace = TRUE)
Color <- factor(Color)
Możemy zaobserwować każdą z wyżej wymienionych cech Color
:
#* 1. It is stored internally as an `integer` vector
typeof(Color)
[1] "integer"
#* 2. It maintains a `levels` attribute the shows the character representation of the values.
#* 3. Its class is stored as `factor`
attributes(Color)
$levels [1] "Blue" "Green" "Red" "Yellow" $class [1] "factor"
Podstawową zaletą obiektu czynnikowego jest wydajność przechowywania danych. Liczba całkowita wymaga mniej pamięci do przechowywania niż znak. Taka wydajność była wysoce pożądana, gdy wiele komputerów miało znacznie bardziej ograniczone zasoby niż obecne maszyny (aby uzyskać bardziej szczegółową historię motywacji związanych z użyciem czynników, zobacz stringsAsFactors
: nieautoryzowana biografia ). Różnicę w użyciu pamięci widać nawet w naszym obiekcie Color
. Jak widać, przechowywanie Color
jako postaci wymaga około 1,7 razy więcej pamięci niż obiekt czynnikowy.
#* Amount of memory required to store Color as a factor.
object.size(Color)
4624 bytes
#* Amount of memory required to store Color as a character
object.size(as.character(Color))
8232 bytes
Mapowanie liczby całkowitej na poziom
Podczas gdy wewnętrzne obliczanie czynników widzi obiekt jako liczbę całkowitą, pożądaną reprezentacją do spożycia przez ludzi jest poziom postaci. Na przykład,
head(Color)
[1] Blue Blue Green Yellow Red Yellow Levels: Blue Green Red Yellow
jest łatwiejszy do zrozumienia dla ludzi niż
head(as.numeric(Color))
[1] 1 1 2 4 3 4
Przybliżona ilustracja tego, jak R idzie o dopasowanie reprezentacji znaków do wewnętrznej wartości całkowitej, jest następująca:
head(levels(Color)[as.numeric(Color)])
[1] "Blue" "Blue" "Green" "Yellow" "Red" "Yellow"
Porównaj te wyniki z
head(Color)
[1] Blue Blue Green Yellow Red Yellow Levels: Blue Green Red Yellow
Nowoczesne wykorzystanie czynników
W 2007 r. R wprowadził metodę mieszania znaków w celu zmniejszenia obciążenia pamięci wektorów znaków (zob .: stringsAsFactors
: an Unauthorized Biography ). Zwróć uwagę, że kiedy ustaliliśmy, że znaki wymagają 1,7 razy więcej miejsca niż czynniki, obliczono to w najnowszej wersji R, co oznacza, że użycie wektorów znaków w pamięci było jeszcze bardziej obciążające przed 2007 rokiem.
Dzięki metodzie haszowania we współczesnym języku R i znacznie większym zasobom pamięci we współczesnych komputerach kwestia wydajności pamięci w przechowywaniu wartości postaci została zredukowana do bardzo małego problemu. W większości sytuacji dominująca postawa w społeczności R ma pierwszeństwo przed wektorami postaci nad czynnikami. Głównymi przyczynami odejścia od czynników są
- Wzrost nieustrukturyzowanych i / lub luźno kontrolowanych danych postaci
- Tendencja czynników, które nie zachowują się zgodnie z oczekiwaniami, gdy użytkownik zapomina, że ma do czynienia z czynnikiem, a nie z postacią
W pierwszym przypadku nie ma sensu przechowywanie dowolnego tekstu lub otwartych pól odpowiedzi jako czynników, ponieważ prawdopodobnie nie będzie żadnego wzorca, który pozwalałby na więcej niż jedną obserwację na poziom. Alternatywnie, jeśli struktura danych nie jest dokładnie kontrolowana, możliwe jest uzyskanie wielu poziomów odpowiadających tej samej kategorii (takich jak „niebieski”, „niebieski” i „niebieski”). W takich przypadkach wielu woli zarządzać tymi rozbieżnościami jako postaci przed konwersją na czynnik (jeśli konwersja w ogóle ma miejsce).
W drugim przypadku, jeśli użytkownik uważa, że pracuje z wektorem znaków, niektóre metody mogą nie reagować zgodnie z oczekiwaniami. To podstawowe zrozumienie może prowadzić do zamieszania i frustracji podczas próby debugowania skryptów i kodów. Chociaż, ściśle rzecz biorąc, można to uznać za winę użytkownika, większość użytkowników chętnie rezygnuje z użycia czynników i całkowicie eliminuje te sytuacje.
Podstawowe tworzenie czynników
Czynniki są jednym ze sposobów reprezentowania zmiennych kategorialnych w R. Czynnik jest przechowywany wewnętrznie jako wektor liczb całkowitych . Unikalne elementy dostarczonego wektora znaków są znane jako poziomy współczynnika. Domyślnie, jeśli poziomy nie są dostarczane przez użytkownika, wówczas R wygeneruje zestaw unikalnych wartości w wektorze, posortuje te wartości alfanumerycznie i użyje ich jako poziomów.
charvar <- rep(c("n", "c"), each = 3)
f <- factor(charvar)
f
levels(f)
> f
[1] n n n c c c
Levels: c n
> levels(f)
[1] "c" "n"
Jeśli chcesz zmienić kolejność poziomów, jedną z opcji jest ręczne określenie poziomów:
levels(factor(charvar, levels = c("n","c")))
> levels(factor(charvar, levels = c("n","c")))
[1] "n" "c"
Czynniki mają wiele właściwości. Na przykład poziomy mogą mieć etykiety:
> f <- factor(charvar, levels=c("n", "c"), labels=c("Newt", "Capybara"))
> f
[1] Newt Newt Newt Capybara Capybara Capybara
Levels: Newt Capybara
Inną właściwością, którą można przypisać, jest to, czy czynnik jest uporządkowany:
> Weekdays <- factor(c("Monday", "Wednesday", "Thursday", "Tuesday", "Friday", "Sunday", "Saturday"))
> Weekdays
[1] Monday Wednesday Thursday Tuesday Friday Sunday Saturday
Levels: Friday Monday Saturday Sunday Thursday Tuesday Wednesday
> Weekdays <- factor(Weekdays, levels=c("Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"), ordered=TRUE)
> Weekdays
[1] Monday Wednesday Thursday Tuesday Friday Sunday Saturday
Levels: Monday < Tuesday < Wednesday < Thursday < Friday < Saturday < Sunday
Gdy poziom współczynnika nie jest już używany, możesz go upuścić za pomocą funkcji droplevels()
:
> Weekend <- subset(Weekdays, Weekdays == "Saturday" | Weekdays == "Sunday")
> Weekend
[1] Sunday Saturday
Levels: Monday < Tuesday < Wednesday < Thursday < Friday < Saturday < Sunday
> Weekend <- droplevels(Weekend)
> Weekend
[1] Sunday Saturday
Levels: Saturday < Sunday
Konsolidacja poziomów czynników z listą
Są chwile, w których pożądane jest konsolidowanie poziomów czynników w mniejszych grupach, być może z powodu rzadkich danych w jednej z kategorii. Może się również zdarzyć, gdy masz różne pisownię lub wielkie litery nazw kategorii. Rozważ jako przykład czynnik
set.seed(1)
colorful <- sample(c("red", "Red", "RED", "blue", "Blue", "BLUE", "green", "gren"),
size = 20,
replace = TRUE)
colorful <- factor(colorful)
Ponieważ R rozróżnia wielkość liter, tabela częstotliwości tego wektora wyglądałaby jak poniżej.
table(colorful)
colorful blue Blue BLUE green gren red Red RED 3 1 4 2 4 1 3 2
Ta tabela nie przedstawia jednak prawdziwego rozkładu danych, a kategorie można skutecznie zredukować do trzech typów: niebieskiego, zielonego i czerwonego. Podano trzy przykłady. Pierwszy ilustruje to, co wydaje się oczywistym rozwiązaniem, ale w rzeczywistości nie zapewnia rozwiązania. Drugi daje działające rozwiązanie, ale jest pełny i drogi pod względem obliczeniowym. Trzecie nie jest oczywistym rozwiązaniem, ale jest stosunkowo kompaktowe i wydajne obliczeniowo.
Poziomy konsolidacyjne przy użyciu factor
( factor_approach
)
factor(as.character(colorful),
levels = c("blue", "Blue", "BLUE", "green", "gren", "red", "Red", "RED"),
labels = c("Blue", "Blue", "Blue", "Green", "Green", "Red", "Red", "Red"))
[1] Green Blue Red Red Blue Red Red Red Blue Red Green Green Green Blue Red Green [17] Red Green Green Red Levels: Blue Blue Blue Green Green Red Red Red Warning message: In `levels<-`(`*tmp*`, value = if (nl == nL) as.character(labels) else paste0(labels, : duplicated levels in factors are deprecated
Zauważ, że istnieją zduplikowane poziomy. Wciąż mamy trzy kategorie dla „Niebieskiego”, co nie kończy naszego zadania polegającego na konsolidacji poziomów. Dodatkowo pojawia się ostrzeżenie, że zduplikowane poziomy są przestarzałe, co oznacza, że ten kod może generować błąd w przyszłości.
Konsolidacja poziomów za pomocą ifelse
( ifelse_approach
)
factor(ifelse(colorful %in% c("blue", "Blue", "BLUE"),
"Blue",
ifelse(colorful %in% c("green", "gren"),
"Green",
"Red")))
[1] Green Blue Red Red Blue Red Red Red Blue Red Green Green Green Blue Red Green [17] Red Green Green Red Levels: Blue Green Red
Ten kod generuje pożądany wynik, ale wymaga użycia zagnieżdżonych instrukcji ifelse
. Chociaż w tym podejściu nie ma nic złego, zarządzanie zagnieżdżonymi instrukcjami ifelse
może być żmudnym zadaniem i musi być wykonane ostrożnie.
Konsolidacja poziomów czynników za pomocą listy ( list_approach
)
Mniej oczywistym sposobem konsolidacji poziomów jest użycie listy, w której nazwa każdego elementu jest nazwą żądanej kategorii, a element jest wektorem znaków poziomów w czynniku, który powinien być odwzorowany na pożądaną kategorię. Ma to tę dodatkową zaletę, że działa bezpośrednio na atrybucie levels
czynnika, bez konieczności przypisywania nowych obiektów.
levels(colorful) <-
list("Blue" = c("blue", "Blue", "BLUE"),
"Green" = c("green", "gren"),
"Red" = c("red", "Red", "RED"))
[1] Green Blue Red Red Blue Red Red Red Blue Red Green Green Green Blue Red Green [17] Red Green Green Red Levels: Blue Green Red
Benchmarking każdego podejścia
Czas potrzebny na wykonanie każdego z tych podejść podsumowano poniżej. (Ze względu na miejsce kod do wygenerowania tego podsumowania nie jest wyświetlany)
Unit: microseconds expr min lq mean median uq max neval cld factor 78.725 83.256 93.26023 87.5030 97.131 218.899 100 b ifelse 104.494 107.609 123.53793 113.4145 128.281 254.580 100 c list_approach 49.557 52.955 60.50756 54.9370 65.132 138.193 100 a
Podejście ifelse
działa około dwa razy szybciej ifelse
podejście ifelse
. Jednak z wyjątkiem bardzo bardzo dużych ilości danych różnice w czasie wykonywania będą prawdopodobnie mierzone w mikrosekundach lub milisekundach. Przy tak niewielkich różnicach czasowych wydajność nie musi decydować o wyborze podejścia. Zamiast tego zastosuj podejście, które jest znane i wygodne, i które ty i twoi współpracownicy zrozumiecie w przyszłości.
Czynniki
Czynniki są jedną z metod reprezentowania zmiennych kategorialnych w R. Biorąc pod uwagę wektor x
którego wartości można konwertować na znaki za pomocą as.character()
, domyślne argumenty argumentu factor()
i as.factor()
przypisują liczbę całkowitą do każdego odrębnego elementu wektor oraz atrybut poziomu i atrybut etykiety. Poziomy są wartościami, które x
może ewentualnie przyjąć, a etykiety mogą być danym elementem lub określone przez użytkownika.
Na przykład, jak działają czynniki, utworzymy czynnik z domyślnymi atrybutami, następnie poziomy niestandardowe, a następnie poziomy niestandardowe i etykiety.
# standard
factor(c(1,1,2,2,3,3))
[1] 1 1 2 2 3 3
Levels: 1 2 3
Mogą wystąpić sytuacje, w których użytkownik wie, ile możliwych wartości może przyjąć czynnik, jest większy niż bieżące wartości w wektorze. W tym celu sami przypisujemy poziomy w factor()
.
factor(c(1,1,2,2,3,3),
levels = c(1,2,3,4,5))
[1] 1 1 2 2 3 3
Levels: 1 2 3 4 5
Ze względów stylowych użytkownik może chcieć przypisać etykiety do każdego poziomu. Domyślnie etykiety reprezentują znaki poziomów. Tutaj przypisujemy etykiety dla każdego z możliwych poziomów współczynnika.
factor(c(1,1,2,2,3,3),
levels = c(1,2,3,4,5),
labels = c("Fox","Dog","Cow","Brick","Dolphin"))
[1] Fox Fox Dog Dog Cow Cow
Levels: Fox Dog Cow Brick Dolphin
Zwykle współczynniki można porównywać tylko za pomocą ==
i !=
I jeśli czynniki mają takie same poziomy. Poniższe porównanie czynników nie powiedzie się, mimo że wydają się równe, ponieważ czynniki mają różne poziomy czynników.
factor(c(1,1,2,2,3,3),levels = c(1,2,3)) == factor(c(1,1,2,2,3,3),levels = c(1,2,3,4,5))
Error in Ops.factor(factor(c(1, 1, 2, 2, 3, 3), levels = c(1, 2, 3)), :
level sets of factors are different
Ma to sens, ponieważ dodatkowe poziomy w RHS oznaczają, że R nie ma wystarczającej ilości informacji o każdym czynniku, aby porównać je w znaczący sposób.
Operatory <
, <=
, >
i >=
są użyteczne tylko dla czynników uporządkowanych. Mogą reprezentować wartości jakościowe, które nadal mają porządek liniowy. Współczynnik uporządkowany można utworzyć, podając argument funkcji ordered = TRUE
do funkcji factor
lub po prostu używając funkcji ordered
.
x <- factor(1:3, labels = c('low', 'medium', 'high'), ordered = TRUE)
print(x)
[1] low medium high
Levels: low < medium < high
y <- ordered(3:1, labels = c('low', 'medium', 'high'))
print(y)
[1] high medium low
Levels: low < medium < high
x < y
[1] TRUE FALSE FALSE
Aby uzyskać więcej informacji, zobacz dokumentację Factor .
Czynniki zmiany i zmiany kolejności
Kiedy czynniki są tworzone z wartościami domyślnymi, levels
są tworzone przez as.character
zastosowane do danych wejściowych i są uporządkowane alfabetycznie.
charvar <- rep(c("W", "n", "c"), times=c(17,20,14))
f <- factor(charvar)
levels(f)
# [1] "c" "n" "W"
W niektórych sytuacjach dopuszczalna jest domyślna kolejność levels
(kolejność alfabetyczna / leksykalna). Na przykład, jeśli ktoś po prostu chce plot
częstotliwości, będzie to wynik:
plot(f,col=1:length(levels(f)))
Ale jeśli chcemy mieć inną kolejność levels
, musimy to określić w parametrze levels
lub labels
(uważając, że znaczenie słowa „kolejność” tutaj różni się od czynników uporządkowanych , patrz poniżej). Istnieje wiele alternatywnych sposobów wykonania tego zadania w zależności od sytuacji.
1. Przedefiniuj współczynnik
Gdy jest to możliwe, możemy odtworzyć czynnik, używając parametru levels
z żądaną kolejnością.
ff <- factor(charvar, levels = c("n", "W", "c"))
levels(ff)
# [1] "n" "W" "c"
gg <- factor(charvar, levels = c("W", "c", "n"))
levels(gg)
# [1] "W" "c" "n"
Gdy poziomy wejściowe są inne niż pożądane poziomy wyjściowe, używamy parametru labels
, który powoduje, że parametr levels
staje się „filtrem” dla akceptowalnych wartości wejściowych, ale pozostawia końcowe wartości „poziomów” dla wektora czynnikowego jako argument labels
:
fm <- factor(as.numeric(f),levels = c(2,3,1),
labels = c("nn", "WW", "cc"))
levels(fm)
# [1] "nn" "WW" "cc"
fm <- factor(LETTERS[1:6], levels = LETTERS[1:4], # only 'A'-'D' as input
labels = letters[1:4]) # but assigned to 'a'-'d'
fm
#[1] a b c d <NA> <NA>
#Levels: a b c d
2. Użyj funkcji relevel
Gdy istnieje jeden konkretny level
który musi być pierwszy, możemy użyć relevel
. Dzieje się tak na przykład w kontekście analizy statystycznej, gdy kategoria base
jest niezbędna do testowania hipotezy.
g<-relevel(f, "n") # moves n to be the first level
levels(g)
# [1] "n" "c" "W"
Jak można zweryfikować, f
i g
są takie same
all.equal(f, g)
# [1] "Attributes: < Component “levels”: 2 string mismatches >"
all.equal(f, g, check.attributes = F)
# [1] TRUE
3. Czynniki zmiany kolejności
Są przypadki, gdy musimy reorder
levels
na podstawie liczby, wyniku częściowego, obliczonej statystyki lub wcześniejszych obliczeń. Zmieńmy kolejność na podstawie częstotliwości levels
table(g)
# g
# n c W
# 20 14 17
Funkcja zmiany reorder
jest ogólna (patrz help(reorder)
), ale w tym kontekście potrzebuje: x
, w tym przypadku współczynnik; X
, wartość liczbowa o tej samej długości co x
; i FUN
, funkcja do zastosowania do X
i obliczona na podstawie poziomu x
, który określa kolejność levels
, domyślnie rosnąca. Wynik jest taki sam, jak w przypadku zmiany kolejności poziomów.
g.ord <- reorder(g,rep(1,length(g)), FUN=sum) #increasing
levels(g.ord)
# [1] "c" "W" "n"
Aby uzyskać malejące zamówienie, bierzemy pod uwagę wartości ujemne ( -1
)
g.ord.d <- reorder(g,rep(-1,length(g)), FUN=sum)
levels(g.ord.d)
# [1] "n" "W" "c"
Ponownie czynnik jest taki sam jak inne.
data.frame(f,g,g.ord,g.ord.d)[seq(1,length(g),by=5),] #just same lines
# f g g.ord g.ord.d
# 1 W W W W
# 6 W W W W
# 11 W W W W
# 16 W W W W
# 21 n n n n
# 26 n n n n
# 31 n n n n
# 36 n n n n
# 41 c c c c
# 46 c c c c
# 51 c c c c
Gdy istnieje zmienna ilościowa związana ze zmienną czynnikową, moglibyśmy użyć innych funkcji do zmiany kolejności levels
. Weźmy dane iris
( help("iris")
aby uzyskać więcej informacji), w celu zmiany kolejności współczynnika Species
przy użyciu jego średniej wartości Sepal.Width
.
miris <- iris #help("iris") # copy the data
with(miris, tapply(Sepal.Width,Species,mean))
# setosa versicolor virginica
# 3.428 2.770 2.974
miris$Species.o<-with(miris,reorder(Species,-Sepal.Width))
levels(miris$Species.o)
# [1] "setosa" "virginica" "versicolor"
Zwykły boxplot
(powiedzmy: za with(miris, boxplot(Petal.Width~Species)
) pokaże especies w tej kolejności: setosa , versicolor i virginica . Ale używając uporządkowanego czynnika otrzymujemy gatunek uporządkowany według średniej Sepal.Width
:
boxplot(Petal.Width~Species.o, data = miris,
xlab = "Species", ylab = "Petal Width",
main = "Iris Data, ordered by mean sepal width", varwidth = TRUE,
col = 2:4)
Ponadto można również zmieniać nazwy levels
, łączyć je w grupy lub dodawać nowe levels
. W tym celu korzystamy z funkcji o tych samych levels
nazw.
f1<-f
levels(f1)
# [1] "c" "n" "W"
levels(f1) <- c("upper","upper","CAP") #rename and grouping
levels(f1)
# [1] "upper" "CAP"
f2<-f1
levels(f2) <- c("upper","CAP", "Number") #add Number level, which is empty
levels(f2)
# [1] "upper" "CAP" "Number"
f2[length(f2):(length(f2)+5)]<-"Number" # add cases for the new level
table(f2)
# f2
# upper CAP Number
# 33 17 6
f3<-f1
levels(f3) <- list(G1 = "upper", G2 = "CAP", G3 = "Number") # The same using list
levels(f3)
# [1] "G1" "G2" "G3"
f3[length(f3):(length(f3)+6)]<-"G3" ## add cases for the new level
table(f3)
# f3
# G1 G2 G3
# 33 17 7
- uporządkowane czynniki
Wreszcie wiemy, że ordered
czynniki różnią się od factors
, pierwszy służy do reprezentowania danych porządkowych , a drugi do pracy z danymi nominalnymi . Na początku zmiana sensu levels
dla uporządkowanych czynników nie ma sensu, ale możemy zmienić jego labels
.
ordvar<-rep(c("Low", "Medium", "High"), times=c(7,2,4))
of<-ordered(ordvar,levels=c("Low", "Medium", "High"))
levels(of)
# [1] "Low" "Medium" "High"
of1<-of
levels(of1)<- c("LOW", "MEDIUM", "HIGH")
levels(of1)
# [1] "LOW" "MEDIUM" "HIGH"
is.ordered(of1)
# [1] TRUE
of1
# [1] LOW LOW LOW LOW LOW LOW LOW MEDIUM MEDIUM HIGH HIGH HIGH HIGH
# Levels: LOW < MEDIUM < HIGH
Czynniki przebudowujące od zera
Problem
Współczynniki są używane do reprezentowania zmiennych, które pobierają wartości z zestawu kategorii, znanych jako Poziomy w R. Na przykład, niektóre eksperymenty mogą charakteryzować się poziomem energii akumulatora, z czterema poziomami: pustym, niskim, normalnym i pełnym. Następnie, dla 5 różnych miejsc pobierania próbek, poziomy te można określić następującymi terminami:
pełny , pełny , normalny , pusty , niski
Zazwyczaj w bazach danych lub innych źródłach informacji przetwarzanie tych danych odbywa się za pomocą dowolnych wskaźników całkowitych powiązanych z kategoriami lub poziomami. Jeśli założymy, że dla podanego przykładu przypisalibyśmy następujące wskaźniki: 1 = pusty, 2 = niski, 3 = normalny, 4 = pełny, wówczas 5 próbek można zakodować jako:
4 , 4 , 3 , 1 , 2
Może się zdarzyć, że z twojego źródła informacji, np. Bazy danych, masz tylko zakodowaną listę liczb całkowitych i katalog łączący każdą liczbę całkowitą z każdym słowem kluczowym poziomu. Jak można zrekonstruować czynnik R na podstawie tych informacji?
Rozwiązanie
Będziemy symulować wektor 20 liczb całkowitych reprezentujących próbki, z których każda może mieć jedną z czterech różnych wartości:
set.seed(18)
ii <- sample(1:4, 20, replace=T)
ii
[1] 4 3 4 1 1 3 2 3 2 1 3 4 1 2 4 1 3 1 4 1
Pierwszym krokiem jest utworzenie współczynnika z poprzedniej sekwencji, w którym poziomy lub kategorie są dokładnie liczbami od 1 do 4.
fii <- factor(ii, levels=1:4) # it is necessary to indicate the numeric levels
fii
[1] 4 3 4 1 1 3 2 3 2 1 3 4 1 2 4 1 3 1 4 1
Poziomy: 1 2 3 4
Teraz wystarczy ubrać czynnik już utworzony za pomocą tagów indeksu:
levels(fii) <- c("empty", "low", "normal", "full")
fii
[1] pełny normalny pełny pusty pusty normalny niski normalny niski pusty
[11] normalna pełna pusta niska pełna pusta normalna pusta pełna pusta
Poziomy: pusty niski normalny pełny