ΠΠ½ΠΈΠΌΠΈΡΠ°Π½ΠΈΡΠ΅ Π»Π΅Π½ΡΠΎΠ²ΠΈ Π΄ΠΈΠ°Π³ΡΠ°ΠΌΠΈ, ΠΊΠΎΠΈΡΠΎ ΠΌΠΎΠ³Π°Ρ Π΄Π° Π±ΡΠ΄Π°Ρ Π²Π³ΡΠ°Π΄Π΅Π½ΠΈ Π΄ΠΈΡΠ΅ΠΊΡΠ½ΠΎ Π² ΠΏΡΠ±Π»ΠΈΠΊΠ°ΡΠΈΡ Π½Π° Π²ΡΠ΅ΠΊΠΈ ΡΠ΅Π±ΡΠ°ΠΉΡ, ΡΡΠ°Π²Π°Ρ Π²ΡΠ΅ ΠΏΠΎ-ΠΏΠΎΠΏΡΠ»ΡΡΠ½ΠΈ. Π’Π΅ ΠΏΠΎΠΊΠ°Π·Π²Π°Ρ Π΄ΠΈΠ½Π°ΠΌΠΈΠΊΠ°ΡΠ° Π½Π° ΠΏΡΠΎΠΌΠ΅Π½ΠΈΡΠ΅ Π²ΡΠ² Π²ΡΡΠΊΠ°ΠΊΠ²ΠΈ Ρ
Π°ΡΠ°ΠΊΡΠ΅ΡΠΈΡΡΠΈΠΊΠΈ Π·Π° ΠΎΠΏΡΠ΅Π΄Π΅Π»Π΅Π½ΠΎ Π²ΡΠ΅ΠΌΠ΅ ΠΈ ΠΏΡΠ°Π²ΡΡ ΡΠΎΠ²Π° ΡΡΠ½ΠΎ. ΠΠ΅ΠΊΠ° Π²ΠΈΠ΄ΠΈΠΌ ΠΊΠ°ΠΊ Π΄Π° Π³ΠΈ ΡΡΠ·Π΄Π°Π΄Π΅ΠΌ Ρ ΠΏΠΎΠΌΠΎΡΡΠ° Π½Π° R ΠΈ ΠΎΠ±ΡΠΈ ΠΏΠ°ΠΊΠ΅ΡΠΈ.
Skillbox ΠΏΡΠ΅ΠΏΠΎΡΡΡΠ²Π°: ΠΡΠ°ΠΊΡΠΈΡΠ΅ΡΠΊΠΈ ΠΊΡΡΡ
βΠ Π°Π·ΡΠ°Π±ΠΎΡΡΠΈΠΊ Π½Π° Python ΠΎΡ Π½ΡΠ»Π°ΡΠ°β .ΠΠ°ΠΏΠΎΠΌΠ½ΡΠΌΠ΅ Π²ΠΈ: Π·Π° Π²ΡΠΈΡΠΊΠΈ ΡΠΈΡΠ°ΡΠ΅Π»ΠΈ Π½Π° "Habr" - ΠΎΡΡΡΡΠΏΠΊΠ° ΠΎΡ 10 000 ΡΡΠ±Π»ΠΈ ΠΏΡΠΈ Π·Π°ΠΏΠΈΡΠ²Π°Π½Π΅ Π²ΡΠ² Π²ΡΠ΅ΠΊΠΈ ΠΊΡΡΡ Skillbox, ΠΈΠ·ΠΏΠΎΠ»Π·Π²Π°ΠΉΠΊΠΈ ΠΏΡΠΎΠΌΠΎΡΠΈΠΎΠ½Π°Π»Π½ΠΈΡ ΠΊΠΎΠ΄ Π½Π° "Habr".
ΠΠ°ΠΊΠ΅ΡΠΈ
ΠΠΌΠ°ΠΌΠ΅ Π½ΡΠΆΠ΄Π° ΠΎΡ ΠΏΠ°ΠΊΠ΅ΡΠΈ Π² R:
- ggplot2
-
gganimate
Π’Π΅Π·ΠΈ Π΄Π²Π΅ΡΠ΅ ΡΠ° ΠΈΠ·ΠΊΠ»ΡΡΠΈΡΠ΅Π»Π½ΠΎ Π½Π΅ΠΎΠ±Ρ ΠΎΠ΄ΠΈΠΌΠΈ. ΠΡΠ²Π΅Π½ ΡΠΎΠ²Π° ΡΠ΅ ΡΠ° Π½Π΅ΠΎΠ±Ρ ΠΎΠ΄ΠΈΠΌΠΈ tidyverse, janitor ΠΈ Π²Π΅Π·Π½ΠΈ Π·Π° ΡΠΏΡΠ°Π²Π»Π΅Π½ΠΈΠ΅ Π½Π° Π΄Π°Π½Π½ΠΈΡΠ΅, ΠΏΠΎΡΠΈΡΡΠ²Π°Π½Π΅ Π½Π° ΠΌΠ°ΡΠΈΠ²Π° ΠΈ ΡΡΠΎΡΠ²Π΅ΡΠ½ΠΎ ΡΠΎΡΠΌΠ°ΡΠΈΡΠ°Π½Π΅.
ΠΠ°Π½Π½ΠΈ
ΠΡΠΈΠ³ΠΈΠ½Π°Π»Π½ΠΈΡΡ Π½Π°Π±ΠΎΡ ΠΎΡ Π΄Π°Π½Π½ΠΈ, ΠΊΠΎΠΉΡΠΎ ΡΠ΅ ΠΈΠ·ΠΏΠΎΠ»Π·Π²Π°ΠΌΠ΅ Π² ΡΠΎΠ·ΠΈ ΠΏΡΠΎΠ΅ΠΊΡ, Π΅ ΠΈΠ·ΡΠ΅Π³Π»Π΅Π½ ΠΎΡ ΡΠ΅Π±ΡΠ°ΠΉΡΠ° Π½Π° Π‘Π²Π΅ΡΠΎΠ²Π½Π°ΡΠ° Π±Π°Π½ΠΊΠ°. ΠΡΠΎ Π³ΠΈ ΠΈ ΡΡΡ
-
Π©ΠΎ Π·Π° ΠΈΠ½ΡΠΎΡΠΌΠ°ΡΠΈΡ Π΅ ΡΠΎΠ²Π°? ΠΠ·Π²Π°Π΄ΠΊΠ°ΡΠ° ΡΡΠ΄ΡΡΠΆΠ° ΡΡΠΎΠΉΠ½ΠΎΡΡΡΠ° Π½Π° ΠΠΠ Π½Π° ΠΏΠΎΠ²Π΅ΡΠ΅ΡΠΎ ΡΡΡΠ°Π½ΠΈ Π·Π° Π½ΡΠΊΠΎΠ»ΠΊΠΎ Π³ΠΎΠ΄ΠΈΠ½ΠΈ (ΠΎΡ 2000 Π³. Π΄ΠΎ 2017 Π³.).
ΠΠ±ΡΠ°Π±ΠΎΡΠΊΠ° Π½Π° Π΄Π°Π½Π½ΠΈ
Π©Π΅ ΠΈΠ·ΠΏΠΎΠ»Π·Π²Π°ΠΌΠ΅ ΠΊΠΎΠ΄Π°, ΠΏΡΠ±Π»ΠΈΠΊΡΠ²Π°Π½ ΠΏΠΎ-Π΄ΠΎΠ»Ρ, Π·Π° Π΄Π° ΠΏΠΎΠ΄Π³ΠΎΡΠ²ΠΈΠΌ Π½Π΅ΠΎΠ±Ρ ΠΎΠ΄ΠΈΠΌΠΈΡ ΡΠΎΡΠΌΠ°Ρ Π½Π° Π΄Π°Π½Π½ΠΈΡΠ΅. ΠΠ·ΡΠΈΡΡΠ²Π°ΠΌΠ΅ ΠΈΠΌΠ΅Π½Π°ΡΠ° Π½Π° ΠΊΠΎΠ»ΠΎΠ½ΠΈΡΠ΅, ΠΏΡΠ΅Π²ΡΡΡΠ°ΠΌΠ΅ ΡΠΈΡΠ»Π°ΡΠ° Π² ΡΠΈΡΠ»ΠΎΠ² ΡΠΎΡΠΌΠ°Ρ ΠΈ ΠΏΡΠ΅ΠΎΠ±ΡΠ°Π·ΡΠ²Π°ΠΌΠ΅ Π΄Π°Π½Π½ΠΈΡΠ΅ Ρ ΠΏΠΎΠΌΠΎΡΡΠ° Π½Π° ΡΡΠ½ΠΊΡΠΈΡΡΠ° gather(). ΠΠ°ΠΏΠ°Π·Π²Π°ΠΌΠ΅ Π²ΡΠΈΡΠΊΠΎ ΠΏΠΎΠ»ΡΡΠ΅Π½ΠΎ Π² gdp_tidy.csv Π·Π° ΠΏΠΎ-Π½Π°ΡΠ°ΡΡΡΠ½Π° ΡΠΏΠΎΡΡΠ΅Π±Π°.
library(tidyverse)
library(janitor)
gdp <- read_csv("./data/GDP_Data.csv")
#select required columns
gdp <- gdp %>% select(3:15)
#filter only country rows
gdp <- gdp[1:217,]
gdp_tidy <- gdp %>%
mutate_at(vars(contains("YR")),as.numeric) %>%
gather(year,value,3:13) %>%
janitor::clean_names() %>%
mutate(year = as.numeric(stringr::str_sub(year,1,4)))
write_csv(gdp_tidy,"./data/gdp_tidy.csv")
ΠΠ½ΠΈΠΌΠΈΡΠ°Π½ΠΈ Ρ ΠΈΡΡΠΎΠ³ΡΠ°ΠΌΠΈ
Π‘ΡΠ·Π΄Π°Π²Π°Π½Π΅ΡΠΎ ΠΈΠΌ Π²ΠΊΠ»ΡΡΠ²Π° Π΄Π²Π° Π΅ΡΠ°ΠΏΠ°:
- ΠΡΠ°ΡΠΈΠΊ Π½Π° ΠΏΡΠ»Π΅Π½ Π½Π°Π±ΠΎΡ ΠΎΡ Π΄Π΅ΠΉΡΡΠ²ΠΈΡΠ΅Π»Π½ΠΈ Ρ ΠΈΡΡΠΎΠ³ΡΠ°ΠΌΠΈ Ρ ΠΏΠΎΠΌΠΎΡΡΠ° Π½Π° ggplot2.
- ΠΠ½ΠΈΠΌΠΈΡΠ°ΠΉΡΠ΅ ΡΡΠ°ΡΠΈΡΠ½ΠΈ Ρ ΠΈΡΡΠΎΠ³ΡΠ°ΠΌΠΈ Ρ ΠΆΠ΅Π»Π°Π½ΠΈ ΠΏΠ°ΡΠ°ΠΌΠ΅ΡΡΠΈ Ρ ΠΏΠΎΠΌΠΎΡΡΠ° Π½Π° gganimate.
ΠΠΎΡΠ»Π΅Π΄Π½Π°ΡΠ° ΡΡΡΠΏΠΊΠ° Π΅ Π΄Π° ΠΈΠ·ΠΎΠ±ΡΠ°Π·ΠΈΡΠ΅ Π°Π½ΠΈΠΌΠ°ΡΠΈΡΡΠ° Π² ΠΆΠ΅Π»Π°Π½ΠΈΡ ΡΠΎΡΠΌΠ°Ρ, Π²ΠΊΠ»ΡΡΠΈΡΠ΅Π»Π½ΠΎ GIF ΠΈΠ»ΠΈ MP4.
ΠΠ°ΡΠ΅ΠΆΠ΄Π°Π½Π΅ Π½Π° Π±ΠΈΠ±Π»ΠΈΠΎΡΠ΅ΠΊΠΈ
- Π±ΠΈΠ±Π»ΠΈΠΎΡΠ΅ΠΊΠ° (tidyverse)
- Π±ΠΈΠ±Π»ΠΈΠΎΡΠ΅ΠΊΠ° (gganimate)
Π£ΠΏΡΠ°Π²Π»Π΅Π½ΠΈΠ΅ Π½Π° Π΄Π°Π½Π½ΠΈ
Π ΡΠ°Π·ΠΈ ΡΡΡΠΏΠΊΠ° ΡΡΡΠ±Π²Π° Π΄Π° ΡΠΈΠ»ΡΡΠΈΡΠ°ΡΠ΅ Π΄Π°Π½Π½ΠΈΡΠ΅, Π·Π° Π΄Π° ΠΏΠΎΠ»ΡΡΠΈΡΠ΅ ΠΏΡΡΠ²ΠΈΡΠ΅ 10 Π΄ΡΡΠΆΠ°Π²ΠΈ Π·Π° Π²ΡΡΠΊΠ° Π³ΠΎΠ΄ΠΈΠ½Π°. ΠΠ΅ΠΊΠ° Π΄ΠΎΠ±Π°Π²ΠΈΠΌ Π½ΡΠΊΠΎΠ»ΠΊΠΎ ΠΊΠΎΠ»ΠΎΠ½ΠΈ, ΠΊΠΎΠΈΡΠΎ ΡΠ΅ Π½ΠΈ ΠΏΠΎΠ·Π²ΠΎΠ»ΡΡ Π΄Π° ΠΏΠΎΠΊΠ°ΠΆΠ΅ΠΌ Π»Π΅Π³Π΅Π½Π΄Π° Π·Π° Ρ ΠΈΡΡΠΎΠ³ΡΠ°ΠΌΠ°ΡΠ°.
gdp_tidy <- read_csv("./data/gdp_tidy.csv")
gdp_formatted <- gdp_tidy %>%
group_by(year) %>%
# The * 1 makes it possible to have non-integer ranks while sliding
mutate(rank = rank(-value),
Value_rel = value/value[rank==1],
Value_lbl = paste0(" ",round(value/1e9))) %>%
group_by(country_name) %>%
filter(rank <=10) %>%
ungroup()
ΠΠ·Π³ΡΠ°ΠΆΠ΄Π°Π½Π΅ Π½Π° ΡΡΠ°ΡΠΈΡΠ½ΠΈ Ρ ΠΈΡΡΠΎΠ³ΡΠ°ΠΌΠΈ
Π‘Π΅Π³Π°, ΠΊΠΎΠ³Π°ΡΠΎ ΠΈΠΌΠ°ΠΌΠ΅ ΠΏΠ°ΠΊΠ΅Ρ ΠΎΡ Π΄Π°Π½Π½ΠΈ Π² Π½Π΅ΠΎΠ±Ρ ΠΎΠ΄ΠΈΠΌΠΈΡ ΡΠΎΡΠΌΠ°Ρ, Π·Π°ΠΏΠΎΡΠ²Π°ΠΌΠ΅ Π΄Π° ΡΠΈΡΡΠ²Π°ΠΌΠ΅ ΡΡΠ°ΡΠΈΡΠ½ΠΈ Ρ ΠΈΡΡΠΎΠ³ΡΠ°ΠΌΠΈ. ΠΡΠ½ΠΎΠ²Π½Π° ΠΈΠ½ΡΠΎΡΠΌΠ°ΡΠΈΡ - ΡΠΎΠΏ 10 Π½Π° ΡΡΡΠ°Π½ΠΈΡΠ΅ Ρ ΠΌΠ°ΠΊΡΠΈΠΌΠ°Π»Π΅Π½ ΠΠΠ Π·Π° ΠΈΠ·Π±ΡΠ°Π½ΠΈΡ ΠΈΠ½ΡΠ΅ΡΠ²Π°Π» ΠΎΡ Π²ΡΠ΅ΠΌΠ΅. Π‘ΡΠ·Π΄Π°Π²Π°ΠΌΠ΅ Π³ΡΠ°ΡΠΈΠΊΠΈ Π·Π° Π²ΡΡΠΊΠ° Π³ΠΎΠ΄ΠΈΠ½Π°.
staticplot = ggplot(gdp_formatted, aes(rank, group = country_name,
fill = as.factor(country_name), color = as.factor(country_name))) +
geom_tile(aes(y = value/2,
height = value,
width = 0.9), alpha = 0.8, color = NA) +
geom_text(aes(y = 0, label = paste(country_name, " ")), vjust = 0.2, hjust = 1) +
geom_text(aes(y=value,label = Value_lbl, hjust=0)) +
coord_flip(clip = "off", expand = FALSE) +
scale_y_continuous(labels = scales::comma) +
scale_x_reverse() +
guides(color = FALSE, fill = FALSE) +
theme(axis.line=element_blank(),
axis.text.x=element_blank(),
axis.text.y=element_blank(),
axis.ticks=element_blank(),
axis.title.x=element_blank(),
axis.title.y=element_blank(),
legend.position="none",
panel.background=element_blank(),
panel.border=element_blank(),
panel.grid.major=element_blank(),
panel.grid.minor=element_blank(),
panel.grid.major.x = element_line( size=.1, color="grey" ),
panel.grid.minor.x = element_line( size=.1, color="grey" ),
plot.title=element_text(size=25, hjust=0.5, face="bold", colour="grey", vjust=-1),
plot.subtitle=element_text(size=18, hjust=0.5, face="italic", color="grey"),
plot.caption =element_text(size=8, hjust=0.5, face="italic", color="grey"),
plot.background=element_blank(),
plot.margin = margin(2,2, 2, 4, "cm"))
Π‘ΡΠ·Π΄Π°Π²Π°Π½Π΅ΡΠΎ Π½Π° Π³ΡΠ°ΡΠΈΠΊΠΈ Ρ ΠΏΠΎΠΌΠΎΡΡΠ° Π½Π° ggplot2 Π΅ ΠΌΠ½ΠΎΠ³ΠΎ ΠΏΡΠΎΡΡΠΎ. ΠΠ°ΠΊΡΠΎ ΠΌΠΎΠΆΠ΅ΡΠ΅ Π΄Π° Π²ΠΈΠ΄ΠΈΡΠ΅ Π² ΡΠ΅ΠΊΡΠΈΡΡΠ° Ρ ΠΊΠΎΠ΄ ΠΏΠΎ-Π³ΠΎΡΠ΅, ΠΈΠΌΠ° Π½ΡΠΊΠΎΠ»ΠΊΠΎ ΠΊΠ»ΡΡΠΎΠ²ΠΈ ΡΠΎΡΠΊΠΈ Ρ ΡΡΠ½ΠΊΡΠΈΡΡΠ° theme(). Π’Π΅ ΡΠ° Π½Π΅ΠΎΠ±Ρ ΠΎΠ΄ΠΈΠΌΠΈ, ΡΠ°ΠΊΠ° ΡΠ΅ Π²ΡΠΈΡΠΊΠΈ Π΅Π»Π΅ΠΌΠ΅Π½ΡΠΈ Π΄Π° ΠΎΠΆΠΈΠ²ΡΠ²Π°Ρ Π±Π΅Π· ΠΏΡΠΎΠ±Π»Π΅ΠΌΠΈ. ΠΡΠΊΠΎΠΈ ΠΎΡ ΡΡΡ ΠΌΠΎΠΆΠ΅ Π΄Π° Π½Π΅ Π±ΡΠ΄Π°Ρ ΠΏΠΎΠΊΠ°Π·Π°Π½ΠΈ, Π°ΠΊΠΎ Π΅ Π½Π΅ΠΎΠ±Ρ ΠΎΠ΄ΠΈΠΌΠΎ. ΠΡΠΈΠΌΠ΅Ρ: ΠΠ°ΡΠ΅ΡΡΠ°Π½ΠΈ ΡΠ° ΡΠ°ΠΌΠΎ Π²Π΅ΡΡΠΈΠΊΠ°Π»Π½ΠΈΡΠ΅ Π»ΠΈΠ½ΠΈΠΈ Π½Π° ΠΌΡΠ΅ΠΆΠ°ΡΠ° ΠΈ Π»Π΅Π³Π΅Π½Π΄ΠΈΡΠ΅, Π½ΠΎ Π·Π°Π³Π»Π°Π²ΠΈΡΡΠ° Π½Π° ΠΎΡΠΈΡΠ΅ ΠΈ Π½ΡΠΊΠΎΠ»ΠΊΠΎ Π΄ΡΡΠ³ΠΈ ΠΊΠΎΠΌΠΏΠΎΠ½Π΅Π½ΡΠ° ΡΠ° ΠΏΡΠ΅ΠΌΠ°Ρ Π½Π°ΡΠΈ ΠΎΡ ΠΎΠ±Π»Π°ΡΡΡΠ°.
Π°Π½ΠΈΠΌΠ°ΡΠΈΡ
ΠΠ»ΡΡΠΎΠ²Π°ΡΠ° ΡΡΠ½ΠΊΡΠΈΡ ΡΡΠΊ Π΅ conversion_states(), ΡΡ ΡΠ²ΡΡΠ·Π²Π° ΠΎΡΠ΄Π΅Π»Π½ΠΈ ΡΡΠ°ΡΠΈΡΠ½ΠΈ Π³ΡΠ°ΡΠΈΠΊΠΈ. view_follow() ΡΠ΅ ΠΈΠ·ΠΏΠΎΠ»Π·Π²Π° Π·Π° ΡΠΈΡΡΠ²Π°Π½Π΅ Π½Π° Π»ΠΈΠ½ΠΈΠΈ Π½Π° ΠΌΡΠ΅ΠΆΠ°ΡΠ°.
anim = staticplot + transition_states(year, transition_length = 4, state_length = 1) +
view_follow(fixed_x = TRUE) +
labs(title = 'GDP per Year : {closest_state}',
subtitle = "Top 10 Countries",
caption = "GDP in Billions USD | Data Source: World Bank Data")
ΠΡΠ΅Π΄Π°Π²Π°Π½Π΅
Π‘Π»Π΅Π΄ ΠΊΠ°ΡΠΎ Π°Π½ΠΈΠΌΠ°ΡΠΈΡΡΠ° Π΅ ΡΡΠ·Π΄Π°Π΄Π΅Π½Π° ΠΈ ΡΡΡ ΡΠ°Π½Π΅Π½Π° Π² ΠΎΠ±Π΅ΠΊΡΠ° anim, Π΅ Π²ΡΠ΅ΠΌΠ΅ Π΄Π° Ρ ΠΈΠ·ΠΎΠ±ΡΠ°Π·ΠΈΡΠ΅ Ρ ΠΏΠΎΠΌΠΎΡΡΠ° Π½Π° ΡΡΠ½ΠΊΡΠΈΡΡΠ° animate(). Π Π΅Π½Π΄ΡΡΡΡ, ΠΈΠ·ΠΏΠΎΠ»Π·Π²Π°Π½ Π² animate(), ΠΌΠΎΠΆΠ΅ Π΄Π° Π±ΡΠ΄Π΅ ΡΠ°Π·Π»ΠΈΡΠ΅Π½ Π² Π·Π°Π²ΠΈΡΠΈΠΌΠΎΡΡ ΠΎΡ ΡΠΈΠΏΠ° Π½Π° Π½Π΅ΠΎΠ±Ρ ΠΎΠ΄ΠΈΠΌΠΈΡ ΠΈΠ·Ρ ΠΎΠ΄Π΅Π½ ΡΠ°ΠΉΠ».
GIF
# For GIF
animate(anim, 200, fps = 20, width = 1200, height = 1000,
renderer = gifski_renderer("gganim.gif"))
MP4
# For MP4
animate(anim, 200, fps = 20, width = 1200, height = 1000,
renderer = ffmpeg_renderer()) -> for_mp4
anim_save("animation.mp4", animation = for_mp4 )
Π Π΅Π·ΡΠ»ΡΠ°Ρ
ΠΠ°ΠΊΡΠΎ ΠΌΠΎΠΆΠ΅ΡΠ΅ Π΄Π° Π²ΠΈΠ΄ΠΈΡΠ΅, Π½ΠΈΡΠΎ ΡΠ»ΠΎΠΆΠ½ΠΎ. Π¦Π΅Π»ΠΈΡΡ ΠΏΡΠΎΠ΅ΠΊΡ Π΅ Π΄ΠΎΡΡΡΠΏΠ΅Π½ Π²
Skillbox ΠΏΡΠ΅ΠΏΠΎΡΡΡΠ²Π°:
- ΠΠ²ΡΠ³ΠΎΠ΄ΠΈΡΠ΅Π½ ΠΏΡΠ°ΠΊΡΠΈΡΠ΅ΡΠΊΠΈ ΠΊΡΡΡ
βΠΠ· ΡΡΠΌ PRO ΡΠ΅Π± ΡΠ°Π·ΡΠ°Π±ΠΎΡΡΠΈΠΊβ .- ΠΠ½Π»Π°ΠΉΠ½ ΠΊΡΡΡ
βC# ΠΏΡΠΎΠ³ΡΠ°ΠΌΠΈΡΡ ΠΎΡ Π½ΡΠ»Π°ΡΠ°β .- ΠΡΠ°ΠΊΡΠΈΡΠ΅ΡΠΊΠΈ Π³ΠΎΠ΄ΠΈΡΠ΅Π½ ΠΊΡΡΡ
βPHP ΡΠ°Π·ΡΠ°Π±ΠΎΡΡΠΈΠΊ ΠΎΡ 0 Π΄ΠΎ PROβ .
ΠΠ·ΡΠΎΡΠ½ΠΈΠΊ: www.habr.com