{"id":457779,"date":"2025-04-28T15:00:53","date_gmt":"2025-04-28T15:00:53","guid":{"rendered":"http:\/\/savepearlharbor.com\/?p=457779"},"modified":"-0001-11-30T00:00:00","modified_gmt":"-0001-11-29T21:00:00","slug":"","status":"publish","type":"post","link":"https:\/\/savepearlharbor.com\/?p=457779","title":{"rendered":"<span>BI \u0443\u043c\u0435\u0440, \u0434\u0430 \u0437\u0434\u0440\u0430\u0432\u0441\u0442\u0432\u0443\u0435\u0442 BI<\/span>"},"content":{"rendered":"<div><!--[--><!--]--><\/div>\n<div id=\"post-content-body\">\n<div>\n<div class=\"article-formatted-body article-formatted-body article-formatted-body_version-2\">\n<div xmlns=\"http:\/\/www.w3.org\/1999\/xhtml\">\n<p>\u0412\u0441\u0435\u0445 \u043f\u0440\u0438\u0432\u0435\u0442\u0441\u0442\u0432\u0443\u044e! \u0417\u043e\u0432\u0443\u0442 \u043c\u0435\u043d\u044f \u041f\u0430\u0432\u0435\u043b, \u0440\u0430\u0431\u043e\u0442\u0430\u044e \u0432 Datapulse. \u0421\u043e\u0437\u0434\u0430\u0435\u043c \u0440\u0430\u0437\u043b\u0438\u0447\u043d\u044b\u0435 \u0440\u0435\u0448\u0435\u043d\u0438\u044f \u0434\u043b\u044f \u0430\u0432\u0442\u043e\u043c\u0430\u0442\u0438\u0437\u0430\u0446\u0438\u0438 DWH.<\/p>\n<p>\u041c\u044b \u0436\u0438\u0432\u0435\u043c \u0432 \u043f\u0435\u0440\u0435\u0445\u043e\u0434\u043d\u043e\u043c \u043f\u0435\u0440\u0438\u043e\u0434\u0435, \u043a\u043e\u0433\u0434\u0430 \u043d\u0430 \u0441\u043c\u0435\u043d\u0443 \u0442\u0440\u0430\u0434\u0438\u0446\u0438\u043e\u043d\u043d\u044b\u043c \u0438\u043d\u0441\u0442\u0440\u0443\u043c\u0435\u043d\u0442\u0430\u043c \u0438 \u043f\u043e\u0434\u0445\u043e\u0434\u0430\u043c \u0432 data-engineer \u043f\u0440\u0438\u0445\u043e\u0434\u044f\u0442 \u043d\u043e\u0432\u044b\u0435. \u0422\u043e, \u0447\u0442\u043e \u0435\u0449\u0435 \u0432\u0447\u0435\u0440\u0430 \u043a\u0430\u0437\u0430\u043b\u043e\u0441\u044c \u0441\u0442\u0430\u043d\u0434\u0430\u0440\u0442\u043e\u043c \u0434\u0435-\u0444\u0430\u043a\u0442\u043e, \u0441\u0435\u0433\u043e\u0434\u043d\u044f \u0432\u0441\u0435 \u0447\u0430\u0449\u0435 \u0432\u043e\u0441\u043f\u0440\u0438\u043d\u0438\u043c\u0430\u0435\u0442\u0441\u044f \u043a\u0430\u043a \u0430\u043d\u0430\u0445\u0440\u043e\u043d\u0438\u0437\u043c. \u041f\u0440\u0438\u043c\u0435\u0440 \u0442\u043e\u043c\u0443 &#8212; \u0432\u043e\u0439\u043d\u0430 GUI (\u00ab\u0433\u0443\u0435\u0432\u044b\u0445\u00bb) ETL \u0438\u043d\u0441\u0442\u0440\u0443\u043c\u0435\u043d\u0442\u043e\u0432 \u0441\u043e \u0441\u043a\u0440\u0438\u043f\u0442\u043e\u0432\u044b\u043c\u0438 \u0437\u0430\u043a\u043e\u043d\u0447\u0438\u043b\u0430\u0441\u044c \u0431\u0435\u0437\u043e\u0433\u043e\u0432\u043e\u0440\u043e\u0447\u043d\u043e\u0439 \u043f\u043e\u0431\u0435\u0434\u043e\u0439 \u043f\u043e\u0441\u043b\u0435\u0434\u043d\u0438\u0445 (\u0432\u0441\u0435 \u0441\u043a\u0430\u0437\u0430\u043d\u043d\u043e\u0435 \u0437\u0434\u0435\u0441\u044c \u044f\u0432\u043b\u044f\u0435\u0442\u0441\u044f \u043c\u043d\u0435\u043d\u0438\u0435\u043c \u0430\u0432\u0442\u043e\u0440\u0430 \u0438 \u043d\u0435 \u043f\u0440\u0435\u0442\u0435\u043d\u0434\u0443\u0435\u0442 \u043d\u0430 \u0438\u0441\u0442\u0438\u043d\u0443 \u0432 \u043f\u043e\u0441\u043b\u0435\u0434\u043d\u0435\u0439 \u0438\u043d\u0441\u0442\u0430\u043d\u0446\u0438\u0438). \u0421\u043a\u0440\u0438\u043f\u0442\u044b \u0432\u0437\u044f\u043b\u0438 \u0432\u0435\u0440\u0445 \u0431\u043b\u0430\u0433\u043e\u0434\u0430\u0440\u044f \u0441\u0432\u043e\u0435\u0439 \u043c\u0430\u0441\u0448\u0442\u0430\u0431\u0438\u0440\u0443\u0435\u043c\u043e\u0441\u0442\u0438, \u0432\u043e\u0437\u043c\u043e\u0436\u043d\u043e\u0441\u0442\u0438 \u043f\u0440\u0438\u043c\u0435\u043d\u0435\u043d\u0438\u044f Git \u0438 \u043b\u0443\u0447\u0448\u0435\u0439 \u0438\u043d\u0442\u0435\u0433\u0440\u0430\u0446\u0438\u0438 \u0432 \u043f\u0440\u043e\u0446\u0435\u0441\u0441\u044b CI\/CD. <\/p>\n<p>\u0410 \u0447\u0442\u043e \u0436\u0435 \u0441 BI? \u041c\u044b \u043f\u0440\u0438\u0432\u044b\u043a\u043b\u0438 \u043a \u043c\u0430\u0441\u0442\u043e\u0434\u043e\u043d\u0442\u0430\u043c: PowerBI, Tableau, Qlik. \u041b\u0438\u0431\u043e open-source: Superset, Metabase. \u041e\u043d\u0438 \u0432 \u0441\u0432\u043e\u0435 \u0432\u0440\u0435\u043c\u044f \u0441\u0442\u0430\u043b\u0438 \u0441\u0442\u0430\u043d\u0434\u0430\u0440\u0442\u043e\u043c, \u0437\u0430\u0445\u0432\u0430\u0442\u0438\u0432 \u0431\u043e\u043b\u044c\u0448\u0443\u044e \u0447\u0430\u0441\u0442\u044c \u0440\u044b\u043d\u043a\u0430, \u0430 sales manager \u043f\u043e\u043b\u0443\u0447\u0430\u043b\u0438 \u043e\u0447\u0435\u043d\u044c \u043d\u0435\u043f\u043b\u043e\u0445\u0438\u0435 \u043f\u0440\u0435\u043c\u0438\u0438 \u0437\u0430 \u0438\u0445 \u0432\u043d\u0435\u0434\u0440\u0435\u043d\u0438\u0435. \u0418, \u043a\u0430\u0437\u0430\u043b\u043e\u0441\u044c, \u0432\u0440\u0435\u043c\u044f \u043c\u043e\u043b\u043e\u0447\u043d\u044b\u0445 \u0440\u0435\u043a \u0438 \u043a\u0438\u0441\u0435\u043b\u044c\u043d\u044b\u0445 \u0431\u0435\u0440\u0435\u0433\u043e\u0432 \u043d\u0435 \u0437\u0430\u043a\u043e\u043d\u0447\u0438\u0442\u0441\u044f \u043d\u0438\u043a\u043e\u0433\u0434\u0430. \u041a\u043e\u043c\u043f\u0430\u043d\u0438\u0438 \u043f\u0440\u043e\u0434\u043e\u043b\u0436\u0430\u0442 \u0442\u0440\u0430\u0442\u0438\u0442\u044c \u043a\u0443\u0447\u0443 \u0434\u0435\u043d\u0435\u0433 \u043d\u0430 \u043a\u0440\u0430\u0441\u0438\u0432\u0435\u043d\u044c\u043a\u0438\u0435 \u0433\u0440\u0430\u0444\u0438\u043a\u0438, \u043a\u043e\u0442\u043e\u0440\u044b\u043c\u0438 \u043d\u0435 \u043f\u043e\u043b\u044c\u0437\u0443\u044e\u0442\u0441\u044f, \u0430 \u0432 \u0441\u043e\u043e\u0431\u0449\u0435\u0441\u0442\u0432\u0435 \u0430\u043d\u0430\u043b\u0438\u0442\u0438\u043a\u043e\u0432 \u0431\u0443\u0434\u0443\u0442 \u043b\u0438\u0448\u044c \u0440\u0430\u0441\u0441\u0443\u0436\u0434\u0430\u0442\u044c \u043e \u0442\u043e\u043c, \u043a\u0430\u043a \u0434\u043e\u043a\u0430\u0437\u0430\u0442\u044c \u0443\u043f\u0435\u0440\u0442\u043e\u043c\u0443 \u043c\u0435\u043d\u0435\u0434\u0436\u0435\u0440\u0443, \u0447\u0442\u043e \u0434\u0430\u0448\u0431\u043e\u0440\u0434-\u0432\u0443\u043d\u0434\u0435\u0440\u0432\u0430\u0444\u043b\u044f \u0432 PowerBI \u0433\u043e\u0440\u0430\u0437\u0434\u043e \u0443\u0434\u043e\u0431\u043d\u0435\u0435 \u0441\u0442\u0430\u0440\u043e\u0433\u043e \u0434\u043e\u0431\u0440\u043e\u0433\u043e Excel (\u0448\u0443\u0442\u043a\u0430).<\/p>\n<p>\u0410 \u0432\u0440\u0435\u043c\u0435\u043d\u0430 \u044d\u0442\u0438, \u0435\u0441\u043b\u0438 \u0438 \u043d\u0435 \u043f\u0440\u043e\u0448\u043b\u0438, \u0442\u043e \u0431\u043b\u0438\u0437\u044f\u0442\u0441\u044f \u043a \u0437\u0430\u043a\u0430\u0442\u0443. \u0421\u0442\u0430\u0440\u044b\u0435 \u043a\u043e\u0440\u043e\u043b\u0438 \u0447\u0430\u0445\u043d\u0443\u0442 \u0438 \u0443\u043c\u0438\u0440\u0430\u044e\u0442. \u041f\u0440\u0438\u0432\u0435\u0442\u0441\u0442\u0432\u0443\u0435\u043c \u043d\u043e\u0432\u044b\u0445! <\/p>\n<figure class=\"full-width\"><img decoding=\"async\" src=\"https:\/\/habrastorage.org\/r\/w1560\/getpro\/habr\/upload_files\/36a\/5c0\/1f4\/36a5c01f43946e8a5bc55a7e81c80a5b.png\" width=\"1135\" height=\"793\" sizes=\"auto, (max-width: 780px) 100vw, 50vw\" srcset=\"https:\/\/habrastorage.org\/r\/w780\/getpro\/habr\/upload_files\/36a\/5c0\/1f4\/36a5c01f43946e8a5bc55a7e81c80a5b.png 780w,&#10;       https:\/\/habrastorage.org\/r\/w1560\/getpro\/habr\/upload_files\/36a\/5c0\/1f4\/36a5c01f43946e8a5bc55a7e81c80a5b.png 781w\" loading=\"lazy\" decode=\"async\"\/><\/figure>\n<p>\u0427\u0435\u043c \u043f\u043b\u043e\u0445\u0438 \u0441\u0442\u0430\u043d\u0434\u0430\u0440\u0442\u043d\u044b\u0435 BI-\u0438\u043d\u0441\u0442\u0440\u0443\u043c\u0435\u043d\u0442\u044b? \u041f\u043e\u0441\u043c\u0435\u044e \u043f\u0435\u0440\u0435\u0447\u0438\u0441\u043b\u0438\u0442\u044c \u0438\u0445 \u043d\u0435\u0434\u043e\u0441\u0442\u0430\u0442\u043a\u0438:<\/p>\n<ul>\n<li>\n<p>\u0441\u043b\u043e\u0436\u043d\u043e\u0435 \u0432\u043d\u0435\u0434\u0440\u0435\u043d\u0438\u0435 \u0438 \u043e\u0431\u0441\u043b\u0443\u0436\u0438\u0432\u0430\u043d\u0438\u0435<\/p>\n<\/li>\n<li>\n<p>\u043c\u0438\u043d\u0438\u043c\u0430\u043b\u044c\u043d\u044b\u0435 \u0432\u043e\u0437\u043c\u043e\u0436\u043d\u043e\u0441\u0442\u0438 \u0438\u043d\u0442\u0435\u0433\u0440\u0430\u0446\u0438\u0438 \u0441 Git (\u0438\u043b\u0438 \u0432\u043e\u043e\u0431\u0449\u0435 \u0438\u0445 \u043e\u0442\u0441\u0443\u0442\u0441\u0442\u0432\u0438\u0435)<\/p>\n<\/li>\n<li>\n<p>\u0441\u043b\u0430\u0431\u044b\u0435 \u0432\u043e\u0437\u043c\u043e\u0436\u043d\u043e\u0441\u0442\u0438 \u043a\u0430\u0441\u0442\u043e\u043c\u0438\u0437\u0430\u0446\u0438\u0438 (\u043f\u043e\u0442\u0440\u0435\u0431\u0443\u0435\u0442\u0441\u044f \u043d\u0435\u0441\u043b\u0430\u0431\u0430\u044f \u044d\u043a\u0441\u043f\u0435\u0440\u0442\u0438\u0437\u0430)<\/p>\n<\/li>\n<li>\n<p>\u0432\u044b\u0441\u043e\u043a\u0430\u044f\/\u043e\u0447\u0435\u043d\u044c \u0432\u044b\u0441\u043e\u043a\u0430\u044f \u0441\u0442\u043e\u0438\u043c\u043e\u0441\u0442\u044c (\u0435\u0441\u043b\u0438 \u043f\u0440\u043e\u043f\u0440\u0438\u0435\u0442\u0430\u0440\u043d\u043e\u0435 \u041f\u041e)<\/p>\n<\/li>\n<li>\n<p>\u0441\u043b\u043e\u0436\u043d\u043e \u0440\u0430\u0437\u043e\u0431\u0440\u0430\u0442\u044c\u0441\u044f \u0438 \u043f\u043e\u0434\u0434\u0435\u0440\u0436\u0438\u0432\u0430\u0442\u044c (\u0435\u0441\u043b\u0438 open-source)<\/p>\n<\/li>\n<\/ul>\n<blockquote>\n<p>&#8212; \u0414\u043e\u0432\u043e\u043b\u044c\u043d\u043e \u043f\u0440\u0435\u0434\u0438\u0441\u043b\u043e\u0432\u0438\u044f! \u0414\u0430\u0436\u0435 \u0435\u0441\u043b\u0438 \u044f \u0441\u043e\u0433\u043b\u0430\u0448\u0443\u0441\u044c \u0441\u043e \u0441\u043a\u0430\u0437\u0430\u043d\u043d\u044b\u043c, \u0447\u0435\u043c \u0437\u0430\u043c\u0435\u043d\u0438\u0442\u044c \u0441\u0442\u0430\u043d\u0434\u0430\u0440\u0442\u043d\u044b\u0439 BI? \u2013 \u0441\u043f\u0440\u043e\u0441\u0438\u0442 \u0447\u0438\u0442\u0430\u0442\u0435\u043b\u044c. <br \/>&#8212; \u0423 \u043c\u0435\u043d\u044f \u0435\u0441\u0442\u044c \u0434\u043b\u044f \u0432\u0430\u0441 \u043f\u0430\u0440\u0430 \u0431\u043b\u044e\u0434! \u2013 \u043e\u0442\u0432\u0435\u0447\u0443 \u044f.<\/p>\n<\/blockquote>\n<p>\u0421\u043a\u0440\u0438\u043f\u0442\u043e\u0432\u044b\u0435 BI \u0438\u043b\u0438 BI as a code. \u041f\u043e\u044f\u0432\u0438\u043b\u0438\u0441\u044c \u043d\u0435 \u0442\u0430\u043a \u0434\u0430\u0432\u043d\u043e, \u043d\u043e \u0443\u0436\u0435 \u043d\u0430\u0431\u0438\u0440\u0430\u044e\u0442 \u0431\u0435\u0448\u0435\u043d\u043d\u0443\u044e \u043f\u043e\u043f\u0443\u043b\u044f\u0440\u043d\u043e\u0441\u0442\u044c. \u0421\u0440\u0430\u0432\u043d\u0438\u0432\u0430\u0442\u044c \u0440\u0430\u0437\u043b\u0438\u0447\u043d\u044b\u0435 \u0440\u0435\u0448\u0435\u043d\u0438\u044f \u0432 \u044d\u0442\u043e\u0439 \u0441\u0442\u0430\u0442\u044c\u044f \u044f \u043d\u0435 \u0431\u0443\u0434\u0443. \u0415\u0441\u0442\u044c \u0437\u0430\u043c\u0435\u0447\u0430\u0442\u0435\u043b\u044c\u043d\u0430\u044f \u043e\u0431\u0437\u043e\u0440\u043d\u0430\u044f <a href=\"https:\/\/motherduck.com\/blog\/the-future-of-bi-bi-as-code-duckdb-impact\/\" rel=\"noopener noreferrer nofollow\">\u0441\u0442\u0430\u0442\u044c\u044f<\/a>, \u0433\u0434\u0435 \u043e\u043f\u0438\u0441\u044b\u0432\u0430\u044e\u0442\u0441\u044f \u043d\u0430\u0438\u0431\u043e\u043b\u0435\u0435 \u043f\u043e\u043f\u0443\u043b\u044f\u0440\u043d\u044b\u0435 \u0444\u0440\u0435\u0439\u043c\u0432\u043e\u0440\u043a\u0438. \u0412\u0441\u0435 \u043f\u0440\u0438\u043c\u0435\u0440\u044b \u0441\u043a\u0440\u0438\u043f\u0442\u043e\u0432\u043e\u0433\u043e BI \u0432 \u044d\u0442\u043e\u0439 \u0441\u0442\u0430\u0442\u044c\u0435 \u0431\u0443\u0434\u0443\u0442 \u043e\u0441\u043d\u043e\u0432\u0430\u043d\u044b \u043d\u0430 \u0444\u0440\u0435\u0439\u043c\u0432\u043e\u0440\u043a\u0435 <code>streamlit<\/code>, \u043f\u043e\u0442\u043e\u043c\u0443 \u0447\u0442\u043e \u0430\u0432\u0442\u043e\u0440 \u0431\u043e\u043b\u0435\u0435 \u0447\u0435\u043c \u0445\u043e\u0440\u043e\u0448\u043e \u0441 \u043d\u0438\u043c \u0437\u043d\u0430\u043a\u043e\u043c.<\/p>\n<p>\u0421\u043f\u0435\u0440\u0432\u0430 \u043e\u043f\u0440\u0435\u0434\u0435\u043b\u0438\u043c \u044f\u0432\u043d\u044b\u0435 \u0434\u043e\u0441\u0442\u043e\u0438\u043d\u0441\u0442\u0432\u0430 \u0441\u043a\u0440\u0438\u043f\u0442\u043e\u0432\u043e\u0433\u043e BI \u0432 \u043e\u0442\u043d\u043e\u0448\u0435\u043d\u0438\u0438 \u0441\u0442\u0430\u043d\u0434\u0430\u0440\u0442\u043d\u043e\u0433\u043e BI: <\/p>\n<ul>\n<li>\n<p>\u042d\u043b\u0435\u043c\u0435\u043d\u0442\u0430\u0440\u043d\u0430\u044f \u0443\u0441\u0442\u0430\u043d\u043e\u0432\u043a\u0430 \u0432 \u043f\u0430\u0440\u0443 \u043a\u043e\u043c\u0430\u043d\u0434<\/p>\n<\/li>\n<li>\n<p>\u041f\u0440\u043e\u0441\u0442\u043e\u0439 \u043e\u0431\u043c\u0435\u043d \u0434\u0430\u0448\u0431\u043e\u0440\u0434\u0430\u043c\u0438 (\u043e\u0431\u043c\u0435\u043d\u0438\u0432\u0430\u0435\u0442\u0435\u0441\u044c \u043b\u0438\u0448\u044c \u0444\u0430\u0439\u043b\u0430\u043c\u0438)<\/p>\n<\/li>\n<li>\n<p>\u0410\u043f\u0440\u0438\u043e\u0440\u0438 \u0434\u0440\u0443\u0436\u0438\u0442 \u0441 Git<\/p>\n<\/li>\n<li>\n<p>\u0411\u0435\u0437\u0433\u0440\u0430\u043d\u0438\u0447\u043d\u044b\u0435 \u0432\u043e\u0437\u043c\u043e\u0436\u043d\u043e\u0441\u0442\u0438 \u043a\u0430\u0441\u0442\u043e\u043c\u0438\u0437\u0430\u0446\u0438\u0438 (\u043c\u043e\u0436\u043d\u043e \u0440\u0435\u0430\u043b\u0438\u0437\u043e\u0432\u0430\u0442\u044c \u043f\u043e\u043b\u043d\u043e\u0446\u0435\u043d\u043d\u044b\u0439 \u0438\u043d\u0442\u0435\u0440\u0444\u0435\u0439\u0441!)<\/p>\n<\/li>\n<li>\n<p>\u0411\u0435\u0441\u043f\u043b\u0430\u0442\u043d\u043e (\u0443\u0441\u043b\u043e\u0432\u043d\u043e)<\/p>\n<\/li>\n<\/ul>\n<h2>\u041f\u0440\u0430\u043a\u0442\u0438\u0447\u0435\u0441\u043a\u043e\u0435 \u043f\u0440\u0438\u043c\u0435\u043d\u0435\u043d\u0438\u0435<\/h2>\n<p>\u041f\u0435\u0440\u0435\u0439\u0434\u0435\u043c \u0436\u0435 \u043a \u043d\u0435\u043f\u043e\u0441\u0440\u0435\u0434\u0441\u0442\u0432\u0435\u043d\u043d\u044b\u043c \u043f\u0440\u0438\u043c\u0435\u0440\u0430\u043c.<\/p>\n<p>\u0423\u0441\u0442\u0430\u043d\u043e\u0432\u043a\u0430 \u0431\u043e\u043b\u0435\u0435 \u0447\u0435\u043c \u043f\u0440\u043e\u0441\u0442\u0430\u044f.<\/p>\n<pre><code class=\"bash\">pip install streamlit<\/code><\/pre>\n<p>\u041f\u0438\u0448\u0435\u043c \u044d\u043b\u0435\u043c\u0435\u043d\u0442\u0430\u0440\u043d\u044b\u0439 \u043a\u043e\u0434 \u0434\u043b\u044f \u0433\u0440\u0430\u0444\u0438\u043a\u0430, \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u0447\u0438\u0442\u0430\u0435\u0442 \u0432\u0438\u0442\u0440\u0438\u043d\u0443 \u0441 \u0437\u0430\u043a\u0430\u0437\u0430\u043c\u0438 \u0438\u0437 PostgreSQL \u0438 \u0432\u0435\u0440\u0441\u0442\u0430\u0435\u0442 \u0433\u0440\u0430\u0444\u0438\u043a.<\/p>\n<pre><code class=\"python\">from sqlalchemy import create_engine import pandas as pd import streamlit as st  # \u0421\u043e\u0437\u0434\u0430\u0435\u043c \u0434\u0432\u0438\u0436\u043e\u043a \u043f\u043e\u0434\u043a\u043b\u044e\u0447\u0435\u043d\u0438\u044f engine = create_engine('postgresql+psycopg2:\/\/postgres:admin@localhost:5432\/database')  # \u0412\u044b\u043f\u043e\u043b\u043d\u044f\u0435\u043c \u0437\u0430\u043f\u0440\u043e\u0441 query = \"\"\" select     order_month,     sum(total_orders) as orders_count from dm.mart_order group by     order_month \"\"\" df = pd.read_sql_query(query, engine) # \u0413\u0440\u0430\u0444\u0438\u043a st.bar_chart(df, x=\"order_month\", y=\"orders_count\", x_label=\"\u041c\u0435\u0441\u044f\u0446\", y_label=\"\u041a\u043e\u043b-\u0432\u043e \u0437\u0430\u043a\u0430\u0437\u043e\u0432\")<\/code><\/pre>\n<p>\u0417\u0430\u043f\u0443\u0441\u043a\u0430\u0435\u043c streamlit \u0432 \u0431\u0440\u0430\u0443\u0437\u0435\u0440\u0435<\/p>\n<pre><code class=\"bash\">streamlit run app.py<\/code><\/pre>\n<figure class=\"full-width\"><img decoding=\"async\" src=\"https:\/\/habrastorage.org\/r\/w1560\/getpro\/habr\/upload_files\/4f1\/9d3\/978\/4f19d3978cd28b84f8f9dd43db664a61.png\" alt=\"\u0420\u0435\u0437\u0443\u043b\u044c\u0442\u0430\u0442\" title=\"\u0420\u0435\u0437\u0443\u043b\u044c\u0442\u0430\u0442\" width=\"974\" height=\"498\" sizes=\"auto, (max-width: 780px) 100vw, 50vw\" srcset=\"https:\/\/habrastorage.org\/r\/w780\/getpro\/habr\/upload_files\/4f1\/9d3\/978\/4f19d3978cd28b84f8f9dd43db664a61.png 780w,&#10;       https:\/\/habrastorage.org\/r\/w1560\/getpro\/habr\/upload_files\/4f1\/9d3\/978\/4f19d3978cd28b84f8f9dd43db664a61.png 781w\" loading=\"lazy\" decode=\"async\"\/><\/p>\n<div><figcaption>\u0420\u0435\u0437\u0443\u043b\u044c\u0442\u0430\u0442<\/figcaption><\/div>\n<\/figure>\n<p>\u041a\u043e\u043d\u0435\u0447\u043d\u043e, \u044d\u043b\u0435\u043c\u0435\u043d\u0442\u0430\u0440\u043d\u044b\u0439 bar chart \u2013 \u0441\u043b\u0438\u0448\u043a\u043e\u043c \u0441\u043a\u0443\u0447\u043d\u043e. \u0414\u0430\u0432\u0430\u0439\u0442\u0435 \u0434\u043e\u0431\u0430\u0432\u0438\u043c \u0441\u044e\u0434\u0430 \u0444\u0438\u043b\u044c\u0442\u0440\u044b \u043f\u043e \u043f\u0435\u0440\u0438\u043e\u0434\u0443 \u0438 \u043f\u0440\u043e\u0434\u0443\u043a\u0442\u0443. \u041e\u0431\u0440\u0430\u0449\u0430\u044e \u0432\u043d\u0438\u043c\u0430\u043d\u0438\u0435, \u0447\u0442\u043e \u0438\u0437 \u043a\u043e\u0440\u043e\u0431\u043a\u0438 <code>streamlit<\/code> \u0443\u043c\u0435\u0435\u0442 \u043a\u044d\u0448\u0438\u0440\u043e\u0432\u0430\u0442\u044c \u0434\u0430\u043d\u043d\u044b\u0435, \u0447\u0442\u043e\u0431\u044b \u0432 \u043d\u0430\u0448\u0435\u043c \u0441\u043b\u0443\u0447\u0430\u0435 \u043d\u0435 \u0432\u044b\u043f\u043e\u043b\u043d\u044f\u0442\u044c \u0442\u044f\u0436\u0435\u043b\u044b\u0435 SQL \u043f\u043e \u043d\u0435\u0441\u043a\u043e\u043b\u044c\u043a\u0443 \u0440\u0430\u0437.<\/p>\n<details class=\"spoiler\">\n<summary>\u0418\u0437\u043c\u0435\u043d\u0435\u043d\u043d\u044b\u0439 python-\u0441\u043a\u0440\u0438\u043f\u0442<\/summary>\n<div class=\"spoiler__content\">\n<pre><code class=\"python\">from sqlalchemy import create_engine import pandas as pd import streamlit as st  # \u0421\u043e\u0437\u0434\u0430\u0435\u043c \u0434\u0432\u0438\u0436\u043e\u043a \u043f\u043e\u0434\u043a\u043b\u044e\u0447\u0435\u043d\u0438\u044f engine = create_engine('postgresql+psycopg2:\/\/postgres:admin@localhost:5432\/databaase')  # \u043f\u043e\u043b\u0443\u0447\u0430\u0435\u043c \u0438 \u043a\u044d\u0448\u0438\u0440\u0443\u0435\u043c \u0434\u0430\u043d\u043d\u044b\u0435 spinner_text = \"\u0427\u0442\u0435\u043d\u0438\u0435 \u0434\u0430\u043d\u043d\u044b\u0445...\" @st.cache_data(ttl=300, max_entries=1000, show_spinner=spinner_text) def get_unique_values(column):     \"\"\"\u041f\u043e\u043b\u0443\u0447\u0430\u0435\u043c \u0443\u043d\u0438\u043a\u0430\u043b\u044c\u043d\u044b\u0435 \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u044f \u0441\u0442\u043e\u043b\u0431\u0446\u0430 \u0432\u0438\u0442\u0440\u0438\u043d\u044b\"\"\"     return  pd.read_sql_query(         f\"select distinct {column} from dm.mart_order order by {column}\",         engine)  @st.cache_data(ttl=300, max_entries=1000, show_spinner=spinner_text) def get_order_data(         date_from = None,         date_to = None,         city = None,         product = None ):     \"\"\"\u041f\u043e\u043b\u0443\u0447\u0430\u0435\u043c \u0434\u0430\u043d\u043d\u044b\u0435 \u0438\u0437 \u0432\u0438\u0442\u0440\u0438\u043d\u044b\"\"\"     filter = ''     if date_from:         filter += f\" and order_month&gt;='{str(date_from)}'\"     if date_to:         filter += f\" and order_month&lt;='{str(date_to)}'\"     if city:         city = \"','\".join(city)         filter += f\" and city_name in ('{city}')\"     if product:         product = \"','\".join(product)         filter += f\" and product_category in ('{str(product)}')\"     query = f\"\"\" select     order_month,     sum(total_orders) as orders_count from dm.mart_order where 1=1     {filter} group by     order_month \"\"\"     return pd.read_sql_query(query, engine)<\/code><\/pre>\n<\/div>\n<\/details>\n<figure class=\"full-width\"><img decoding=\"async\" src=\"https:\/\/habrastorage.org\/r\/w1560\/getpro\/habr\/upload_files\/fb0\/1fb\/af2\/fb01fbaf2d80c413cd9da24b21efa5eb.png\" alt=\"\u0420\u0435\u0437\u0443\u043b\u044c\u0442\u0430\u0442\" title=\"\u0420\u0435\u0437\u0443\u043b\u044c\u0442\u0430\u0442\" width=\"974\" height=\"272\" sizes=\"auto, (max-width: 780px) 100vw, 50vw\" srcset=\"https:\/\/habrastorage.org\/r\/w780\/getpro\/habr\/upload_files\/fb0\/1fb\/af2\/fb01fbaf2d80c413cd9da24b21efa5eb.png 780w,&#10;       https:\/\/habrastorage.org\/r\/w1560\/getpro\/habr\/upload_files\/fb0\/1fb\/af2\/fb01fbaf2d80c413cd9da24b21efa5eb.png 781w\" loading=\"lazy\" decode=\"async\"\/><\/p>\n<div><figcaption>\u0420\u0435\u0437\u0443\u043b\u044c\u0442\u0430\u0442<\/figcaption><\/div>\n<\/figure>\n<p>\u041d\u0430\u0431\u043e\u0440 \u0432\u0438\u0437\u0443\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u0439 \u0432 <code>streamlit<\/code> \u0441\u043a\u0443\u0434\u043d\u044b\u0439, \u043d\u043e \u043c\u043e\u0436\u043d\u043e \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c \u0441\u0442\u043e\u0440\u043e\u043d\u043d\u0438\u0435 \u0431\u0438\u0431\u043b\u0438\u043e\u0442\u0435\u043a\u0438. \u0422\u0430\u043a\u0438\u0435 \u043a\u0430\u043a <code>plotly<\/code>, <code>altair<\/code>, <code>vega lite<\/code> \u0438 \u0434\u0440\u0443\u0433\u0438\u0435. \u041c\u043d\u0435 \u043b\u0438\u0447\u043d\u043e \u043f\u043e \u0434\u0443\u0448\u0435 <code>echarts<\/code>. \u0423 \u043d\u0435\u0433\u043e \u0431\u043e\u043b\u044c\u0448\u0438\u0435 \u0432\u043e\u0437\u043c\u043e\u0436\u043d\u043e\u0441\u0442\u0438, \u043d\u043e \u0434\u043e\u0441\u0442\u0430\u0442\u043e\u0447\u043d\u043e \u0441\u043b\u043e\u0436\u043d\u044b\u0439 \u0438\u043d\u0442\u0435\u0440\u0444\u0435\u0439\u0441 \u0432 \u0432\u0438\u0434\u0435 json-\u043f\u0430\u0440\u0430\u043c\u0435\u0442\u0440\u043e\u0432.<\/p>\n<pre><code class=\"bash\">pip install streamlit-echarts<\/code><\/pre>\n<p>Json-\u043f\u0430\u0440\u0430\u043c\u0435\u0442\u0440\u043e\u0432 \u043e\u0433\u0440\u043e\u043c\u043d\u043e\u0435 \u043a\u043e\u043b\u0438\u0447\u0435\u0441\u0442\u0432\u043e, \u0440\u0430\u0437\u043e\u0431\u0440\u0430\u0442\u044c\u0441\u044f \u0432 \u043d\u0438\u0445 \u0441 \u043d\u0443\u043b\u044f \u0441\u043b\u043e\u0436\u043d\u043e, \u043d\u043e \u043c\u043e\u0436\u043d\u043e \u0443\u043f\u0440\u043e\u0441\u0442\u0438\u0442\u044c \u0441\u0435\u0431\u0435 \u0436\u0438\u0437\u043d\u044c \u0438 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c ChatGPT.<\/p>\n<details class=\"spoiler\">\n<summary>\u041f\u0440\u0438\u043c\u0435\u0440 promt \u0438 \u043e\u0442\u0432\u0435\u0442\u0430<\/summary>\n<div class=\"spoiler__content\">\n<blockquote>\n<p>\u0415\u0441\u0442\u044c pandas.dataframe \u0441\u043e \u0441\u0442\u043e\u043b\u0431\u0446\u0430\u043c\u0438 <code>order_month<\/code>, <code>orders_count<\/code> \u0438 <code>average_order_amount<\/code>. \u0421\u0444\u043e\u0440\u043c\u0438\u0440\u0443\u0439 json \u0441 \u043f\u0430\u0440\u0430\u043c\u0435\u0442\u0440\u0430\u043c\u0438 \u0434\u043b\u044f \u0433\u0440\u0430\u0444\u0438\u043a\u0430 <code>streamlit_echarts<\/code> , \u043f\u043e \u043e\u0441\u0438 X <code>orders_month<\/code>, \u043f\u043e \u043e\u0441\u0438 Y <code>orders_count<\/code> \u0441 \u0442\u0438\u043f\u043e\u043c bar, \u0446\u0432\u0435\u0442 <code>orders_count<\/code> &#8212; <code>#1E90FF<\/code>. \u041f\u043e \u0434\u043e\u043f\u043e\u043b\u043d\u0438\u0442\u0435\u043b\u044c\u043d\u043e\u0439 \u043e\u0441\u0438 Y <code>average_order_amount<\/code> c \u0442\u0438\u043f\u043e\u043c line, \u0446\u0432\u0435\u0442 &#8212; <code>#B0E0E6<\/code>. \u0423 \u0433\u0440\u0430\u0444\u0438\u043a\u0430 \u0434\u043e\u043b\u0436\u0435\u043d \u0431\u044b\u0442\u044c title &#8212; \u0417\u0430\u043a\u0430\u0437\u044b \u043f\u043e \u043c\u0435\u0441\u044f\u0446\u0430\u043c.<\/p>\n<\/blockquote>\n<p>\u041e\u0442\u0432\u0435\u0442 ChatGPT<\/p>\n<pre><code class=\"json\">{     \"title\": {         \"text\": \"\u0417\u0430\u043a\u0430\u0437\u044b \u043f\u043e \u043c\u0435\u0441\u044f\u0446\u0430\u043c\",         \"left\": \"center\",         \"textStyle\": {             \"color\": \"#FFFFFF\"  # \u0426\u0432\u0435\u0442 \u0437\u0430\u0433\u043e\u043b\u043e\u0432\u043a\u0430         }     },     \"tooltip\": {         \"trigger\": \"axis\"     },     \"legend\": {         \"data\": [\"\u041a\u043e\u043b\u0438\u0447\u0435\u0441\u0442\u0432\u043e \u0437\u0430\u043a\u0430\u0437\u043e\u0432\", \"\u0421\u0440\u0435\u0434\u043d\u0438\u0439 \u0447\u0435\u043a\"],          \"bottom\": \"0%\",   # \u041b\u0435\u0433\u0435\u043d\u0434\u0430 \u0432\u043d\u0438\u0437\u0443          \"left\": \"center\", # \u0412\u044b\u0440\u0430\u0432\u043d\u0438\u0432\u0430\u043d\u0438\u0435 \u043f\u043e \u0446\u0435\u043d\u0442\u0440\u0443 \u043f\u043e \u0433\u043e\u0440\u0438\u0437\u043e\u043d\u0442\u0430\u043b\u0438          \"orient\": \"horizontal\",  # \u0413\u043e\u0440\u0438\u0437\u043e\u043d\u0442\u0430\u043b\u044c\u043d\u0430\u044f \u043e\u0440\u0438\u0435\u043d\u0442\u0430\u0446\u0438\u044f         \"textStyle\": {             \"color\": \"#FFFFFF\"  # \u0426\u0432\u0435\u0442 \u0437\u0430\u0433\u043e\u043b\u043e\u0432\u043a\u0430         }     },     \"xAxis\": {         \"type\": \"category\",         \"data\": df['order_month'].tolist()     },     \"yAxis\": [         {             \"type\": \"value\",             \"name\": \"\u041a\u043e\u043b\u0438\u0447\u0435\u0441\u0442\u0432\u043e \u0437\u0430\u043a\u0430\u0437\u043e\u0432\"         },         {             \"type\": \"value\",             \"name\": \"\u0421\u0440\u0435\u0434\u043d\u0438\u0439 \u0447\u0435\u043a\",             \"position\": \"right\"         }     ],     \"series\": [         {             \"name\": \"\u041a\u043e\u043b\u0438\u0447\u0435\u0441\u0442\u0432\u043e \u0437\u0430\u043a\u0430\u0437\u043e\u0432\",             \"type\": \"bar\",             \"data\": df['orders_count'].tolist(),             \"itemStyle\": {                 \"color\": \"#1E90FF\"             },             \"yAxisIndex\": 0         },         {             \"name\": \"\u0421\u0440\u0435\u0434\u043d\u0438\u0439 \u0447\u0435\u043a\",             \"type\": \"line\",             \"data\": df['average_order_amount'].tolist(),             \"itemStyle\": {                 \"color\": \"#B0E0E6\"             },             \"yAxisIndex\": 1         }     ] } <\/code><\/pre>\n<\/div>\n<\/details>\n<figure class=\"full-width\"><img decoding=\"async\" src=\"https:\/\/habrastorage.org\/r\/w1560\/getpro\/habr\/upload_files\/221\/1c7\/37a\/2211c737ad5c82624d08600174fd1ca7.png\" alt=\"\u041e\u0444\u043e\u0440\u043c\u043b\u0435\u043d\u0438\u0435 \u0433\u0440\u0430\u0444\u0438\u043a\u0430 \u0441 echarts\" title=\"\u041e\u0444\u043e\u0440\u043c\u043b\u0435\u043d\u0438\u0435 \u0433\u0440\u0430\u0444\u0438\u043a\u0430 \u0441 echarts\" width=\"974\" height=\"314\" sizes=\"auto, (max-width: 780px) 100vw, 50vw\" srcset=\"https:\/\/habrastorage.org\/r\/w780\/getpro\/habr\/upload_files\/221\/1c7\/37a\/2211c737ad5c82624d08600174fd1ca7.png 780w,&#10;       https:\/\/habrastorage.org\/r\/w1560\/getpro\/habr\/upload_files\/221\/1c7\/37a\/2211c737ad5c82624d08600174fd1ca7.png 781w\" loading=\"lazy\" decode=\"async\"\/><\/p>\n<div><figcaption>\u041e\u0444\u043e\u0440\u043c\u043b\u0435\u043d\u0438\u0435 \u0433\u0440\u0430\u0444\u0438\u043a\u0430 \u0441 echarts<\/figcaption><\/div>\n<\/figure>\n<p>SQL-\u0437\u0430\u043f\u0440\u043e\u0441\u044b \u043b\u0443\u0447\u0448\u0435 \u0445\u0440\u0430\u043d\u0438\u0442\u044c \u043e\u0442\u0434\u0435\u043b\u044c\u043d\u043e \u0438 \u0432\u044b\u0437\u044b\u0432\u0430\u0442\u044c \u0438\u0445 \u043a\u0430\u043a jinja-\u0441\u043a\u0440\u0438\u043f\u0442\u044b. \u0418 \u043f\u0435\u0440\u0435\u043d\u0435\u0441\u0442\u0438 \u0442\u0443\u0434\u0430 \u0432\u0441\u044e \u043b\u043e\u0433\u0438\u043a\u0443 \u0444\u043e\u0440\u043c\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u044f SQL-\u0437\u0430\u043f\u0440\u043e\u0441\u0430 \u043d\u0430 \u043e\u0441\u043d\u043e\u0432\u0430\u043d\u0438\u0438 \u0444\u0438\u043b\u044c\u0442\u0440\u043e\u0432. \u0422\u0430\u043a\u0436\u0435 \u0431\u043e\u043b\u044c\u0448\u0438\u0435 json \u0441 \u043f\u0430\u0440\u0430\u043c\u0435\u0442\u0440\u0430\u043c\u0438 echarts-\u0433\u0440\u0430\u0444\u0438\u043a\u043e\u0432 \u0442\u043e\u0436\u0435 \u0432 \u043e\u0442\u0434\u0435\u043b\u044c\u043d\u044b\u0435 \u0444\u0430\u0439\u043b\u044b.<\/p>\n<details class=\"spoiler\">\n<summary>\u041f\u0440\u0438\u043c\u0435\u0440 jinja-\u0441\u043a\u0440\u0438\u043f\u0442\u0430<\/summary>\n<div class=\"spoiler__content\">\n<pre><code class=\"sql\">select     order_month,     sum(total_orders) as orders_count,     sum(total_order_amount)\/sum(total_orders) as average_order_amount from dm.mart_order where 1=1     {% if date_from %}and order_month&gt;='{{ date_from }}'{% endif %}     {% if date_to %}and order_month&lt;='{{ date_to }}'{% endif %}     {% if city %}and city_name in ({% for item in city %}'{{ item }}'{% if not loop.last %},{% endif %}{% endfor %}){% endif %}     {% if product %}and product_category in ({% for item in product %}'{{ item }}'{% if not loop.last %},{% endif %}{% endfor %}){% endif %} group by     order_month<\/code><\/pre>\n<\/div>\n<\/details>\n<details class=\"spoiler\">\n<summary>\u041f\u0435\u0440\u0435\u043f\u0438\u0441\u0430\u043d\u043d\u044b\u0439 python-\u0441\u043a\u0440\u0438\u043f\u0442 \u0441 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u043d\u0438\u0435\u043c jinja<\/summary>\n<div class=\"spoiler__content\">\n<pre><code class=\"python\">@st.cache_data(ttl=300, max_entries=1000, show_spinner=spinner_text) def get_order_data(         date_from = None,         date_to = None,         city = None,         product = None ):     \"\"\"\u041f\u043e\u043b\u0443\u0447\u0430\u0435\u043c \u0434\u0430\u043d\u043d\u044b\u0435 \u0438\u0437 \u0432\u0438\u0442\u0440\u0438\u043d\u044b\"\"\"     with open('get_order_data.sql', \"r\", encoding=\"utf-8\") as f:         file = f.read()     script = jinja2.Template(file).render(         date_from=str(date_from) if date_from else None,         date_to=str(date_to) if date_to else None,         city=city,         product=product     )     st.write(script)     return pd.read_sql_query(script, engine)<\/code><\/pre>\n<\/div>\n<\/details>\n<p>\u0414\u043e\u0431\u0430\u0432\u0438\u043c \u043d\u0430 \u0434\u0430\u0448\u0431\u043e\u0440\u0434 \u0435\u0449\u0435 \u0433\u0440\u0430\u0444\u0438\u043a\u043e\u0432 \u0434\u043b\u044f \u0431\u043e\u043b\u0435\u0435 \u043a\u0440\u0430\u0441\u043e\u0447\u043d\u043e\u0433\u043e \u043f\u0440\u0438\u043c\u0435\u0440\u0430.<\/p>\n<figure class=\"full-width\"><img decoding=\"async\" src=\"https:\/\/habrastorage.org\/r\/w1560\/getpro\/habr\/upload_files\/38c\/f87\/8c7\/38cf878c7664a311619cdab20f086e18.png\" alt=\"\u041f\u0440\u0438\u043c\u0435\u0440 \u0434\u0430\u0448\u0431\u043e\u0440\u0434\u0430 \u0432 streamlit\" title=\"\u041f\u0440\u0438\u043c\u0435\u0440 \u0434\u0430\u0448\u0431\u043e\u0440\u0434\u0430 \u0432 streamlit\" width=\"974\" height=\"463\" sizes=\"auto, (max-width: 780px) 100vw, 50vw\" srcset=\"https:\/\/habrastorage.org\/r\/w780\/getpro\/habr\/upload_files\/38c\/f87\/8c7\/38cf878c7664a311619cdab20f086e18.png 780w,&#10;       https:\/\/habrastorage.org\/r\/w1560\/getpro\/habr\/upload_files\/38c\/f87\/8c7\/38cf878c7664a311619cdab20f086e18.png 781w\" loading=\"lazy\" decode=\"async\"\/><\/p>\n<div><figcaption>\u041f\u0440\u0438\u043c\u0435\u0440 \u0434\u0430\u0448\u0431\u043e\u0440\u0434\u0430 \u0432 streamlit<\/figcaption><\/div>\n<\/figure>\n<details class=\"spoiler\">\n<summary>Python-\u0441\u043a\u0440\u0438\u043f\u0442<\/summary>\n<div class=\"spoiler__content\">\n<pre><code class=\"python\">from sqlalchemy import create_engine import pandas as pd import streamlit as st from streamlit_echarts import st_echarts import jinja2 import datetime import json from typing import Literal st.set_page_config(layout=\"wide\")  # \u0421\u043e\u0437\u0434\u0430\u0435\u043c \u0434\u0432\u0438\u0436\u043e\u043a \u043f\u043e\u0434\u043a\u043b\u044e\u0447\u0435\u043d\u0438\u044f engine = create_engine('postgresql+psycopg2:\/\/postgres:admin@localhost:5432\/database')  # \u043f\u043e\u043b\u0443\u0447\u0430\u0435\u043c \u0438 \u043a\u044d\u0448\u0438\u0440\u0443\u0435\u043c \u0434\u0430\u043d\u043d\u044b\u0435 spinner_text = \"\u0427\u0442\u0435\u043d\u0438\u0435 \u0434\u0430\u043d\u043d\u044b\u0445...\" @st.cache_data(ttl=300, max_entries=1000, show_spinner=spinner_text) def get_unique_values(column):     \"\"\"\u041f\u043e\u043b\u0443\u0447\u0430\u0435\u043c \u0443\u043d\u0438\u043a\u0430\u043b\u044c\u043d\u044b\u0435 \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u044f \u0441\u0442\u043e\u043b\u0431\u0446\u0430 \u0432\u0438\u0442\u0440\u0438\u043d\u044b\"\"\"     return  pd.read_sql_query(         f\"select distinct {column} from dm.mart_order order by {column}\",         engine)  @st.cache_data(ttl=300, max_entries=1000, show_spinner=spinner_text) def get_extreme_value(column, type: Literal[\"min\", \"max\"]):     \"\"\"\u041f\u043e\u043b\u0443\u0447\u0430\u0435\u043c \u044d\u043a\u0441\u0442\u0440\u0435\u043c\u0443\u043c\u044b\"\"\"     return  pd.read_sql_query(         f\"select {type}({column}) from dm.mart_order\",         engine).iloc[0, 0]  @st.cache_data(ttl=300, max_entries=1000, show_spinner=spinner_text) def get_order_data(         date_from = None,         date_to = None,         city = None,         product = None,         price = None ):     \"\"\"\u041f\u043e\u043b\u0443\u0447\u0430\u0435\u043c \u0434\u0430\u043d\u043d\u044b\u0435 \u0438\u0437 \u0432\u0438\u0442\u0440\u0438\u043d\u044b\"\"\"     with open('get_order_data.sql', \"r\", encoding=\"utf-8\") as f:         file = f.read()     script = jinja2.Template(file).render(         date_from=str(date_from) if date_from else None,         date_to=str(date_to) if date_to else None,         city=city,         product=product,         price=price     )     return pd.read_sql_query(script, engine)  @st.cache_data(ttl=300, max_entries=1000, show_spinner=spinner_text) def get_total_data(         date_from = None,         date_to = None,         city = None,         product = None,         price = None ):     \"\"\"\u041f\u043e\u043b\u0443\u0447\u0430\u0435\u043c \u0434\u0430\u043d\u043d\u044b\u0435 \u0438\u0437 \u0432\u0438\u0442\u0440\u0438\u043d\u044b\"\"\"     with open('get_total_data.sql', \"r\", encoding=\"utf-8\") as f:         file = f.read()     script = jinja2.Template(file).render(         date_from=str(date_from) if date_from else None,         date_to=str(date_to) if date_to else None,         city=city,         product=product,         price=price     )     return pd.read_sql_query(script, engine)  @st.cache_data(ttl=300, max_entries=1000, show_spinner=spinner_text) def get_order_speed(         date_from = None,         date_to = None,         city = None,         product = None,         last = False ):     \"\"\"\u041f\u043e\u043b\u0443\u0447\u0430\u0435\u043c \u0434\u0430\u043d\u043d\u044b\u0435 \u0438\u0437 \u0432\u0438\u0442\u0440\u0438\u043d\u044b\"\"\"     with open('speed.sql', \"r\", encoding=\"utf-8\") as f:         file = f.read()     script = jinja2.Template(file).render(         date_from=str(date_from) if date_from else None,         date_to=str(date_to) if date_to else None,         city=city,         product=product     )     return pd.read_sql_query(script, engine)   # \u0444\u0438\u043b\u044c\u0442\u0440\u0430\u0446\u0438\u044f sidebar = st.sidebar col1, col2 = sidebar.columns((1,1)) with col1:     from_date = st.date_input(\"\u041e\u0442\", value=None) with col2:     to_date = st.date_input(\"\u0414\u043e\", value=None) city = sidebar.multiselect(\"\u0413\u043e\u0440\u043e\u0434\", options=get_unique_values('city_name')) product = sidebar.multiselect(\"\u041f\u0440\u043e\u0434\u0443\u043a\u0442\", options=get_unique_values('product_category')) price = sidebar.slider(     \"\u0426\u0435\u043d\u0430\",     min_value=get_extreme_value(\"total_order_amount\", 'min'),     max_value=get_extreme_value(\"total_order_amount\", 'max'),     step=1.0 )  # \u043c\u0435\u0442\u0440\u0438\u043a\u0438 st.caption('\u0417\u0430 \u043f\u043e\u0441\u043b\u0435\u0434\u043d\u0438\u0439 \u043c\u0435\u0441\u044f\u0446') total_current_data = get_total_data(     from_date,     to_date,     city,     product,     price=price )  col1, col2, col3, col4 = st.columns((1,1,1,1)) with col1:     total_order = \"{:,.0f}\".format(total_current_data['total_orders'].tolist()[0])     st.metric(\"\u041a\u043e\u043b-\u0432\u043e \u0437\u0430\u043a\u0430\u0437\u043e\u0432\", value=total_order, delta=f\"{total_current_data['delta_total_orders'].tolist()[0]} %\") with col2:     total_order_amount = \"{:,.0f}\".format(total_current_data['total_order_amount'].tolist()[0])     st.metric(\"\u041e\u0431\u044a\u0435\u043c \u043f\u0440\u043e\u0434\u0430\u0436\", value=total_order_amount, delta=f\"{total_current_data['delta_total_order_amount'].tolist()[0]} %\") with col3:     average_order_amount = \"{:,.0f}\".format(total_current_data['average_order_amount'].tolist()[0])     st.metric(\"\u0421\u0440\u0435\u0434\u043d\u0435\u0435 \u043a\u043e\u043b-\u0432\u043e \u043f\u043e\u0437\u0438\u0446\u0438\u0439 \u0432 \u0437\u0430\u043a\u0430\u0437\u0435\", value=average_order_amount, delta=f\"{total_current_data['delta_average_order_amount'].tolist()[0]} %\") with col4:     returned_orders = \"{:,.0f}\".format(total_current_data['returned_orders'].tolist()[0])     st.metric(\"\u041a\u043e\u043b-\u0432\u043e \u0432\u043e\u0437\u0432\u0440\u0430\u0442\u043e\u0432\", value=returned_orders, delta=f\"{total_current_data['delta_returned_orders'].tolist()[0]} %\", delta_color='inverse')  # \u0413\u0440\u0430\u0444\u0438\u043a df = get_order_data(     from_date,     to_date,     city,     product,     price=price ) df['order_month'] = df['order_month'].apply(lambda x: x.strftime('%Y-%m'))  last_speed_df = get_order_speed(     from_date,     to_date,     city,     product,     last=True ) speed_df = get_order_speed(     from_date,     to_date,     city,     product ) speed_df['order_month'] = speed_df['order_month'].apply(lambda x: x.strftime('%Y-%m'))  col1, col2 = st.columns((1,1)) with col1:     with open('orders_by_month.json', \"r\", encoding=\"utf-8\") as f:         orders_by_month_json = f.read()     orders_by_month_options = jinja2.Template(orders_by_month_json).render(         order_month=df['order_month'].tolist(),         orders_count=df['orders_count'].tolist(),         average_order_amount=df['average_order_amount'].tolist()     )      st_echarts(options=json.loads(orders_by_month_options), height=\"400px\")  with col2:     with open('returned_by_month.json', \"r\", encoding=\"utf-8\") as f:         returned_by_month = f.read()     returned_by_month_options = jinja2.Template(returned_by_month).render(         order_month=df['order_month'].tolist(),         returned_count=df['returned_count'].tolist(),         returned_percent=df['returned_percent'].tolist(),         max_count=df['orders_count'].max()     )      st_echarts(options=json.loads(returned_by_month_options), height=\"400px\")  col1, col2 = st.columns((1,4)) with col1:     with open('order_speed.json', \"r\", encoding=\"utf-8\") as f:         last_speed_json = f.read()     last_speed_options = jinja2.Template(last_speed_json).render(         speed=last_speed_df['avg_speed'].tolist()[0]     )      st_echarts(options=json.loads(last_speed_options), height=\"400px\") with col2:     with open('order_speed_by_month.json', \"r\", encoding=\"utf-8\") as f:         speed_by_month_json = f.read()     speed_by_month_options = jinja2.Template(speed_by_month_json).render(         speed=speed_df['avg_speed'].tolist(),         order_month=speed_df['order_month'].tolist()     )      st_echarts(options=json.loads(speed_by_month_options), height=\"400px\") <\/code><\/pre>\n<\/div>\n<\/details>\n<p>\u0412\u043e\u043e\u0431\u0449\u0435, \u0443 <code>echarts<\/code> \u043e\u0433\u0440\u043e\u043c\u043d\u043e\u0435 \u043a\u043e\u043b\u0438\u0447\u0435\u0441\u0442\u0432\u043e \u0440\u0430\u0437\u043b\u0438\u0447\u043d\u044b\u0445 <a href=\"https:\/\/echarts.apache.org\/examples\/en\/index.html\" rel=\"noopener noreferrer nofollow\">\u0432\u0430\u0440\u0438\u0430\u043d\u0442\u043e\u0432 <\/a>\u0432\u0438\u0437\u0443\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u0439, \u0447\u0442\u043e \u043d\u0430 \u043e\u0434\u043d\u043e \u043b\u0438\u0448\u044c \u0437\u043d\u0430\u043a\u043e\u043c\u0441\u0442\u0432\u043e \u0441 \u043d\u0438\u043c\u0438 \u0443\u0439\u0434\u0435\u0442 \u043d\u0435\u0441\u043a\u043e\u043b\u044c\u043a\u043e \u0447\u0430\u0441\u043e\u0432.<\/p>\n<p>\u0415\u0441\u0442\u044c \u0432\u043e\u0437\u043c\u043e\u0436\u043d\u043e\u0441\u0442\u044c \u0432\u0435\u0440\u0441\u0442\u0430\u0442\u044c \u043d\u0435 \u0442\u043e\u043b\u044c\u043a\u043e \u0433\u0440\u0430\u0444\u0438\u043a\u0438, \u043d\u043e \u0438 \u0442\u0430\u0431\u043b\u0438\u0446\u044b.<\/p>\n<figure class=\"full-width\"><img decoding=\"async\" src=\"https:\/\/habrastorage.org\/r\/w1560\/getpro\/habr\/upload_files\/802\/15f\/586\/80215f586c42d1d4d0513c9220d632b5.png\" alt=\"\u041f\u0440\u0438\u043c\u0435\u0440 \u0442\u0430\u0431\u043b\u0438\u0446\u044b\" title=\"\u041f\u0440\u0438\u043c\u0435\u0440 \u0442\u0430\u0431\u043b\u0438\u0446\u044b\" width=\"974\" height=\"374\" sizes=\"auto, (max-width: 780px) 100vw, 50vw\" srcset=\"https:\/\/habrastorage.org\/r\/w780\/getpro\/habr\/upload_files\/802\/15f\/586\/80215f586c42d1d4d0513c9220d632b5.png 780w,&#10;       https:\/\/habrastorage.org\/r\/w1560\/getpro\/habr\/upload_files\/802\/15f\/586\/80215f586c42d1d4d0513c9220d632b5.png 781w\" loading=\"lazy\" decode=\"async\"\/><\/p>\n<div><figcaption>\u041f\u0440\u0438\u043c\u0435\u0440 \u0442\u0430\u0431\u043b\u0438\u0446\u044b<\/figcaption><\/div>\n<\/figure>\n<p>\u0412 <code>streamlit<\/code> \u0435\u0441\u0442\u044c \u0432\u043e\u0437\u043c\u043e\u0436\u043d\u043e\u0441\u0442\u044c \u0441\u043e\u0437\u0434\u0430\u0432\u0430\u0442\u044c \u043c\u043d\u043e\u0433\u043e\u0441\u0442\u0440\u0430\u043d\u0438\u0447\u043d\u044b\u0435 \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u044f, \u0435\u0441\u043b\u0438 \u043b\u043e\u0433\u0438\u043a\u0430 \u0432\u0430\u0448\u0435\u0433\u043e \u0434\u0430\u0448\u0431\u043e\u0440\u0434\u0430 \u043d\u0435 \u043f\u043e\u043c\u0435\u0449\u0430\u0435\u0442\u0441\u044f \u043d\u0430 \u043e\u0434\u043d\u0443 \u0441\u0442\u0440\u0430\u043d\u0438\u0446\u0443. <\/p>\n<p>\u0422\u0430\u043a\u0436\u0435 \u043f\u0440\u0438 \u043f\u0435\u0440\u0435\u0445\u043e\u0434\u0435 \u043d\u0430 \u043d\u043e\u0432\u0443\u044e \u0441\u0442\u0440\u0430\u043d\u0438\u0446\u0443 \u043c\u043e\u0436\u043d\u043e \u043f\u0435\u0440\u0435\u0434\u0430\u0432\u0430\u0442\u044c \u0434\u043e\u043f\u043e\u043b\u043d\u0438\u0442\u0435\u043b\u044c\u043d\u044b\u0435 \u043f\u0430\u0440\u0430\u043c\u0435\u0442\u0440\u044b \u0432 url, \u0442\u0435\u043c \u0441\u0430\u043c\u044b\u043c \u0432\u043e\u0441\u0441\u043e\u0437\u0434\u0430\u0432\u0430\u044f \u043f\u043e\u0434\u043e\u0431\u0438\u0435 drill-down. \u041a \u043f\u0440\u0438\u043c\u0435\u0440\u0443, \u0443 \u0432\u0430\u0441 \u0435\u0441\u0442\u044c \u043f\u0440\u0438\u0432\u0435\u0434\u0435\u043d\u043d\u0430\u044f \u0432\u044b\u0448\u0435 \u0442\u0430\u0431\u043b\u0438\u0446\u0430, \u0438 \u0447\u0442\u043e\u0431\u044b \u043f\u043e\u0441\u043c\u043e\u0442\u0440\u0435\u0442\u044c \u043f\u0440\u043e\u0434\u0430\u0436\u0438 \u0432 \u0434\u0435\u0442\u0430\u043b\u044f\u0445 \u043f\u043e \u043a\u043e\u043d\u043a\u0440\u0435\u0442\u043d\u043e\u043c\u0443 \u043f\u0440\u043e\u0434\u0443\u043a\u0442\u0443  \u043c\u043e\u0436\u043d\u043e \u0434\u043e\u0431\u0430\u0432\u0438\u0442\u044c \u0441\u0441\u044b\u043b\u043a\u0443 \u043d\u0430 \u043d\u043e\u0432\u0443\u044e \u0441\u0442\u0440\u0430\u043d\u0438\u0446\u0443, \u0432 url \u043a\u043e\u0442\u043e\u0440\u043e\u0439 \u0431\u0443\u0434\u0443\u0442 \u043f\u0435\u0440\u0435\u0434\u0430\u043d\u043e \u043d\u0430\u0438\u043c\u0435\u043d\u043e\u0432\u0430\u043d\u0438\u0435 \u043f\u0440\u043e\u0434\u0443\u043a\u0442\u0430.<\/p>\n<pre><code class=\"python\">table_data[\"link\"] = table_data.apply(     lambda row: f\"http:\/\/localhost:8501\/product_detail\/?name={row['product_category']}\",     axis=1 )<\/code><\/pre>\n<figure class=\"full-width\"><img decoding=\"async\" src=\"https:\/\/habrastorage.org\/r\/w1560\/getpro\/habr\/upload_files\/e6b\/112\/e76\/e6b112e76ada1fcdfc08f0ea6f8793ef.png\" alt=\"\u0422\u0430\u0431\u043b\u0438\u0446\u0430 \u0441\u043e \u0441\u0441\u044b\u043b\u043a\u043e\u0439\" title=\"\u0422\u0430\u0431\u043b\u0438\u0446\u0430 \u0441\u043e \u0441\u0441\u044b\u043b\u043a\u043e\u0439\" width=\"974\" height=\"381\" sizes=\"auto, (max-width: 780px) 100vw, 50vw\" srcset=\"https:\/\/habrastorage.org\/r\/w780\/getpro\/habr\/upload_files\/e6b\/112\/e76\/e6b112e76ada1fcdfc08f0ea6f8793ef.png 780w,&#10;       https:\/\/habrastorage.org\/r\/w1560\/getpro\/habr\/upload_files\/e6b\/112\/e76\/e6b112e76ada1fcdfc08f0ea6f8793ef.png 781w\" loading=\"lazy\" decode=\"async\"\/><\/p>\n<div><figcaption>\u0422\u0430\u0431\u043b\u0438\u0446\u0430 \u0441\u043e \u0441\u0441\u044b\u043b\u043a\u043e\u0439<\/figcaption><\/div>\n<\/figure>\n<h2>\u0420\u0430\u0437\u0432\u0435\u0440\u0442\u044b\u0432\u0430\u043d\u0438\u0435<\/h2>\n<p>\u0421\u0446\u0435\u043d\u0430\u0440\u0438\u0439 \u0432\u043d\u0435\u0434\u0440\u0435\u043d\u0438\u044f \u043f\u043e\u0434\u043e\u0431\u043d\u043e\u0433\u043e \u0438\u043d\u0441\u0442\u0440\u0443\u043c\u0435\u043d\u0442\u0430 \u043d\u0430 \u043c\u043d\u043e\u0436\u0435\u0441\u0442\u0432\u043e \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u0435\u0439 \u043c\u043d\u0435 \u043b\u0438\u0447\u043d\u043e \u0432\u0438\u0434\u0438\u0442\u0441\u044f \u0441\u043b\u0435\u0434\u0443\u044e\u0449\u0438\u0439 \u2013 \u0441\u043e\u0437\u0434\u0430\u043d\u0438\u0435 \u043f\u043e\u0434 \u043a\u0430\u0436\u0434\u044b\u0439 \u0434\u0430\u0448\u0431\u043e\u0440\u0434 \u0440\u0435\u043f\u043e\u0437\u0438\u0442\u043e\u0440\u0438\u0439 \u0432 Git, \u0438 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u0438 \u0437\u0430\u043f\u0443\u0441\u043a\u0430\u044e\u0442 \u0434\u0430\u0448\u0431\u043e\u0440\u0434\u044b \u043d\u0430 \u0441\u0432\u043e\u0438\u0445 \u043c\u0430\u0448\u0438\u043d\u0430\u0445. \u0423 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044f \u0434\u043e\u043b\u0436\u043d\u043e \u0431\u044b\u0442\u044c \u0434\u043e\u0441\u0442\u0443\u043f \u0432 \u0421\u0423\u0411\u0414 \u0438 \u0443\u0441\u0442\u0430\u043d\u043e\u0432\u043b\u0435\u043d\u043d\u044b\u0439 python. \u0422\u0435\u043c \u0441\u0430\u043c\u044b\u043c \u043c\u044b \u043d\u0438\u0432\u0435\u043b\u0438\u0440\u0443\u0435\u043c \u043c\u043d\u043e\u0436\u0435\u0441\u0442\u0432\u043e \u043f\u0440\u043e\u0431\u043b\u0435\u043c \u0441 \u043d\u0430\u0433\u0440\u0443\u0437\u043a\u043e\u0439 \u043d\u0430 \u0441\u0435\u0440\u0432\u0435\u0440 BI, \u0430 \u0440\u043e\u043b\u0435\u0432\u043e\u0439 \u0434\u043e\u0441\u0442\u0443\u043f \u0440\u0435\u0430\u043b\u0438\u0437\u0443\u0435\u0442\u0441\u044f \u0447\u0435\u0440\u0435\u0437 \u0440\u0430\u0437\u0434\u0430\u0447\u0443 \u043f\u0440\u0430\u0432 \u043a\u043e\u043d\u043a\u0440\u0435\u0442\u043d\u044b\u043c \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044f\u043c \u043a \u043a\u043e\u043d\u043a\u0440\u0435\u0442\u043d\u044b\u043c \u0440\u0435\u043f\u043e\u0437\u0438\u0442\u043e\u0440\u0438\u044f\u043c. <\/p>\n<p><code>Streamlit<\/code> \u043c\u043e\u0436\u043d\u043e \u0440\u0430\u0437\u0432\u0435\u0440\u043d\u0443\u0442\u044c \u0438 \u043d\u0430 \u0441\u0435\u0440\u0432\u0435\u0440\u0435, \u0434\u0430\u0436\u0435 \u043c\u043e\u0436\u043d\u043e \u0440\u0435\u0430\u043b\u0438\u0437\u043e\u0432\u0430\u0442\u044c \u0430\u0443\u0442\u0435\u043d\u0442\u0438\u0444\u0438\u043a\u0430\u0446\u0438\u044e \u0432 \u0438\u043d\u0442\u0435\u0440\u0444\u0435\u0439\u0441\u0435 \u0438 \u0440\u0435\u0430\u043b\u0438\u0437\u043e\u0432\u0430\u0442\u044c \u043a\u044d\u0448\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u0435 \u0434\u0430\u043d\u043d\u044b\u0445 \u043f\u043e\u0434 \u043a\u043e\u043d\u043a\u0440\u0435\u0442\u043d\u0443\u044e \u0441\u0435\u0441\u0441\u0438\u044e, \u043d\u043e \u043f\u0440\u0438 \u0431\u043e\u043b\u044c\u0448\u043e\u043c \u043a\u043e\u043b\u0438\u0447\u0435\u0441\u0442\u0432\u0435 \u043e\u0434\u043d\u043e\u0432\u0440\u0435\u043c\u0435\u043d\u043d\u044b\u0445 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u0435\u0439 \u043d\u0430\u0432\u0435\u0440\u043d\u044f\u043a\u0430 \u0431\u0443\u0434\u0443\u0442 \u043f\u0440\u043e\u0431\u043b\u0435\u043c\u044b.<\/p>\n<p>\u0421 \u0432\u0430\u0440\u0438\u0430\u043d\u0442\u043e\u043c \u0440\u0430\u0437\u0432\u0435\u0440\u0442\u044b\u0432\u0430\u043d\u0438\u044f \u043d\u0430 \u0441\u043e\u0431\u0441\u0442\u0432\u0435\u043d\u043d\u043e\u0439 \u043c\u0430\u0448\u0438\u043d\u0435 \u0438 \u0437\u0430\u043f\u0443\u0441\u043a\u0430 \u0433\u043e\u0442\u043e\u0432\u043e\u0433\u043e \u0434\u0430\u0448\u0431\u043e\u0440\u0434\u0430 (\u043f\u043e \u0438\u043d\u0441\u0442\u0440\u0443\u043a\u0446\u0438\u0438) \u0432 \u0441\u043e\u0441\u0442\u043e\u044f\u043d\u0438\u0438 \u0441\u043f\u0440\u0430\u0432\u0438\u0442\u044c\u0441\u044f \u0434\u0430\u0436\u0435 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044c \u0431\u0435\u0437 \u043a\u0430\u043a\u0438\u0445-\u043b\u0438\u0431\u043e \u0437\u043d\u0430\u043d\u0438\u0439.<\/p>\n<h2>\u0418\u0441\u0442\u043e\u0447\u043d\u0438\u043a\u0438 \u0434\u0430\u043d\u043d\u044b\u0445<\/h2>\n<p>\u0412 \u043f\u0440\u0438\u043c\u0435\u0440\u0430\u0445 \u0432\u044b\u0448\u0435 \u0431\u044b\u043b \u043f\u043e\u043a\u0430\u0437\u0430\u043d \u0432\u0430\u0440\u0438\u0430\u043d\u0442 \u0441 \u043f\u043e\u043b\u0443\u0447\u0435\u043d\u0438\u0435\u043c \u0434\u0430\u043d\u043d\u044b\u0445 \u0438\u0437 \u0445\u0440\u0430\u043d\u0438\u043b\u0438\u0449\u0430 \u043d\u0430 PostgreSQL. \u041a\u043e\u043d\u0435\u0447\u043d\u043e \u0436\u0435, \u043c\u043e\u0436\u043d\u043e \u0447\u0438\u0442\u0430\u0442\u044c \u0434\u0430\u043d\u043d\u044b\u0435 \u0438\u0437 \u0432\u0441\u0435\u0445 \u0421\u0423\u0411\u0414, \u0431\u043b\u0430\u0433\u043e \u0434\u0440\u0430\u0439\u0432\u0435\u0440\u0430 python \u0435\u0441\u0442\u044c \u043f\u0440\u0430\u043a\u0442\u0438\u0447\u0435\u0441\u043a\u0438 \u043f\u043e\u0434 \u043a\u0430\u0436\u0434\u0443\u044e.<\/p>\n<p>\u0422\u0430\u043a\u0436\u0435 \u043c\u043e\u0436\u043d\u043e \u0447\u0438\u0442\u0430\u0442\u044c \u0434\u0430\u043d\u043d\u044b\u0435 \u0438\u0437 CSV, Excel: \u0432 <code>Streamlit<\/code> \u0435\u0441\u0442\u044c \u0432\u0441\u0442\u0440\u043e\u0435\u043d\u043d\u044b\u0439 \u0444\u0443\u043d\u043a\u0446\u0438\u043e\u043d\u0430\u043b \u0437\u0430\u0433\u0440\u0443\u0437\u043a\u0438 \u0444\u0430\u0439\u043b\u043e\u0432. <\/p>\n<p>\u0412 \u0442\u043e\u043c \u0447\u0438\u0441\u043b\u0435 \u043c\u043e\u0436\u043d\u043e \u043f\u0440\u043e\u0434\u0432\u0438\u043d\u0443\u0442\u044c\u0441\u044f \u0434\u0430\u043b\u044c\u0448\u0435 \u0438 \u0441\u0434\u0435\u043b\u0430\u0442\u044c \u0434\u0430\u0448\u0431\u043e\u0440\u0434\u044b \u043d\u0430\u0434 \u0434\u0430\u0442\u0430\u0441\u0435\u0442\u0430\u043c\u0438 \u0438\u0437 DuckDB, \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u0441\u0435\u0439\u0447\u0430\u0441 \u043f\u0440\u043e\u0438\u0437\u0432\u0435\u043b, \u043d\u0430 \u043c\u043e\u0439 \u0441\u043a\u0440\u043e\u043c\u043d\u044b\u0439 \u0432\u0437\u0433\u043b\u044f\u0434, \u0440\u0435\u0432\u043e\u043b\u044e\u0446\u0438\u044e \u0432 data-\u0434\u0432\u0438\u0436\u043a\u0430\u0445. \u041d\u043e \u0437\u0434\u0435\u0441\u044c \u043c\u0430\u0442\u0435\u0440\u0438\u0430\u043b\u0430 \u0445\u0432\u0430\u0442\u0438\u0442 \u043d\u0430 \u043d\u043e\u0432\u0443\u044e \u0441\u0442\u0430\u0442\u044c\u044e, \u043f\u043e\u044d\u0442\u043e\u043c\u0443 \u043d\u0435 \u0431\u0443\u0434\u0435\u043c \u0443\u0433\u043b\u0443\u0431\u043b\u044f\u0442\u044c\u0441\u044f. <\/p>\n<h2>\u041f\u0440\u0438\u043c\u0435\u043d\u0435\u043d\u0438\u0435 \u0438 \u043e\u0433\u0440\u0430\u043d\u0438\u0447\u0435\u043d\u0438\u044f<\/h2>\n<p>\u041a\u0430\u043a\u043e\u0435 \u0436\u0435 \u043f\u0440\u0438\u043c\u0435\u043d\u0435\u043d\u0438\u0435 \u0441\u043a\u0440\u0438\u043f\u0442\u043e\u0432\u044b\u043c BI? \u041c\u043e\u0433\u0443\u0442 \u043b\u0438 \u043e\u043d\u0438 \u043d\u0430 \u0442\u0435\u043a\u0443\u0449\u0438\u0439 \u043c\u043e\u043c\u0435\u043d\u0442 \u044f\u0432\u043b\u044f\u0442\u044c\u0441\u044f \u0437\u0430\u043c\u0435\u043d\u043e\u0439 \u0441\u0442\u0430\u043d\u0434\u0430\u0440\u0442\u043d\u044b\u043c \u0440\u0435\u0448\u0435\u043d\u0438\u044f\u043c? <\/p>\n<p>\u0412\u0430\u0440\u0438\u0430\u043d\u0442\u044b \u043f\u0440\u0438\u043c\u0435\u043d\u0435\u043d\u0438\u044f:<\/p>\n<ul>\n<li>\n<p>\u043d\u0443\u0436\u043d\u043e \u0431\u044b\u0441\u0442\u0440\u043e \u0434\u043b\u044f \u043c\u0435\u043d\u0435\u0434\u0436\u043c\u0435\u043d\u0442\u0430 \u0440\u0435\u0430\u043b\u0438\u0437\u043e\u0432\u0430\u0442\u044c \u0434\u0430\u0448\u0431\u043e\u0440\u0434<\/p>\n<\/li>\n<li>\n<p>\u043c\u043d\u043e\u0433\u043e data-\u043a\u043e\u043c\u0430\u043d\u0434 \u0440\u0430\u0431\u043e\u0442\u0430\u044e\u0442 \u043d\u0430\u0434 \u0434\u0430\u0448\u0431\u043e\u0440\u0434\u0430\u043c\u0438, \u043f\u043e\u044d\u0442\u043e\u043c\u0443 \u0442\u0440\u0435\u0431\u0443\u0435\u0442\u0441\u044f \u043a\u043e\u043d\u0442\u0440\u043e\u043b\u044c \u0432\u0435\u0440\u0441\u0438\u0439 \u0438 \u0432\u043e\u0437\u043c\u043e\u0436\u043d\u043e\u0441\u0442\u044c \u043e\u0442\u043a\u0430\u0442\u0430<\/p>\n<\/li>\n<li>\n<p>\u0431\u0435\u0441\u043f\u043b\u0430\u0442\u043d\u044b\u0439 \u0438\u043d\u0441\u0442\u0440\u0443\u043c\u0435\u043d\u0442 \u0441 \u0431\u044b\u0441\u0442\u0440\u044b\u043c \u0432\u043d\u0435\u0434\u0440\u0435\u043d\u0438\u0435\u043c<\/p>\n<\/li>\n<li>\n<p>\u0430\u0432\u0442\u043e\u043c\u0430\u0442\u0438\u0437\u0430\u0446\u0438\u044f \u0440\u0430\u0437\u0432\u0435\u0440\u0442\u044b\u0432\u0430\u043d\u0438\u044f (CI\/CD)<\/p>\n<\/li>\n<li>\n<p> \u043f\u0435\u0440\u0435\u043d\u043e\u0441 \u043d\u0430\u0433\u0440\u0443\u0437\u043a\u0438 \u043d\u0430 \u043c\u0430\u0448\u0438\u043d\u044b \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u0435\u0439<\/p>\n<\/li>\n<li>\n<p>\u043a\u043e\u0433\u0434\u0430 \u0442\u0440\u0435\u0431\u0443\u044e\u0442\u0441\u044f \u0448\u0438\u0440\u043e\u043a\u0438\u0435 \u0432\u043e\u0437\u043c\u043e\u0436\u043d\u043e\u0441\u0442\u0438 \u043a\u0430\u0441\u0442\u043e\u043c\u0438\u0437\u0430\u0446\u0438\u0438<\/p>\n<\/li>\n<li>\n<p>\u043f\u0440\u0435\u043a\u0440\u0430\u0441\u043d\u043e \u0432\u043f\u0438\u0441\u044b\u0432\u0430\u0435\u0442\u0441\u044f \u0432 Data mesh<\/p>\n<\/li>\n<\/ul>\n<p>\u041e\u0433\u0440\u0430\u043d\u0438\u0447\u0435\u043d\u0438\u044f, \u043a\u043e\u043d\u0435\u0447\u043d\u043e \u0436\u0435, \u0442\u043e\u0436\u0435 \u0435\u0441\u0442\u044c:     <\/p>\n<ul>\n<li>\n<p>\u043c\u043e\u0436\u043d\u043e, \u043d\u043e \u0441\u043b\u043e\u0436\u043d\u043e \u0440\u0430\u0437\u0432\u0435\u0440\u043d\u0443\u0442\u044c \u043d\u0430 \u0441\u0435\u0440\u0432\u0435\u0440\u0435 (\u043f\u043e\u0442\u0440\u0435\u0431\u0443\u0435\u0442\u0441\u044f \u0430\u0443\u0442\u0435\u043d\u0442\u0438\u0444\u0438\u043a\u0430\u0446\u0438\u044f, \u043a\u044d\u0448 \u0432 \u0440\u0430\u043c\u043a\u0430\u0445 \u0441\u0435\u0441\u0441\u0438\u0438 \u0438, \u0441\u043a\u043e\u0440\u0435\u0435 \u0432\u0441\u0435\u0433\u043e, \u0431\u043e\u043b\u044c\u0448\u043e\u0433\u043e \u043a\u043e\u043b\u0438\u0447\u0435\u0441\u0442\u0432\u0430 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u0435\u0439 \u043d\u0435 \u0432\u044b\u0434\u0435\u0440\u0436\u0438\u0442)<\/p>\n<\/li>\n<li>\n<p>\u043d\u0435 \u043f\u043e\u0434\u0445\u043e\u0434\u0438\u0442 \u0434\u043b\u044f Self-service \u0430\u043d\u0430\u043b\u0438\u0442\u0438\u043a\u0438 \u043f\u043e \u043f\u0440\u0438\u0447\u0438\u043d\u0435 \u0441\u0432\u043e\u0435\u0439 \u0441\u043b\u043e\u0436\u043d\u043e\u0441\u0442\u0438 (\u0445\u043e\u0442\u044f \u044f \u043b\u0438\u0447\u043d\u043e \u0432 Self-service \u0430\u043d\u0430\u043b\u0438\u0442\u0438\u043a\u0443 \u0432 \u043f\u0440\u0438\u043d\u0446\u0438\u043f\u0435 \u043d\u0435 \u0432\u0435\u0440\u044e)<\/p>\n<\/li>\n<li>\n<p>\u043d\u0435\u043a\u043e\u0442\u043e\u0440\u044b\u0435 \u0444\u0438\u0447\u0438 \u043e\u0442\u0441\u0443\u0442\u0441\u0442\u0432\u0443\u044e\u0442: drag-n-drop, drill-though \u0438\u043b\u0438 drill-down (\u0445\u043e\u0442\u044f, \u0432\u0440\u043e\u0434\u0435 \u0431\u044b, \u0432\u043e \u0444\u0440\u0435\u0439\u043c\u0432\u043e\u0440\u043a\u0435 Rill \u043c\u043e\u0436\u043d\u043e \u0441\u0434\u0435\u043b\u0430\u0442\u044c \u043f\u043e\u0441\u043b\u0435\u0434\u043d\u0438\u0435 \u0434\u0432\u0430)<\/p>\n<\/li>\n<li>\n<p>\u0431\u043e\u043b\u0435\u0435 \u0441\u043b\u043e\u0436\u043d\u043e \u0440\u0430\u0441\u0441\u0447\u0438\u0442\u0430\u0442\u044c \u0432\u044b\u0447\u0438\u0441\u043b\u044f\u0435\u043c\u044b\u0435 \u043c\u0435\u0440\u044b (\u0434\u043e\u043f\u0443\u0441\u0442\u0438\u043c, \u0432 DAX \u0432\u0435\u0441\u044c\u043c\u0430 \u043f\u0440\u043e\u0441\u0442\u043e \u043f\u043e\u0441\u0447\u0438\u0442\u0430\u0442\u044c \u0434\u0435\u043b\u044c\u0442\u0443 \u043a \u043f\u0440\u043e\u0448\u043b\u043e\u043c\u0443 \u043f\u0435\u0440\u0438\u043e\u0434\u0443, \u0432 SQL \u044d\u0442\u043e \u0441\u0434\u0435\u043b\u0430\u0442\u044c \u0433\u043e\u0440\u0430\u0437\u0434\u043e \u0441\u043b\u043e\u0436\u043d\u0435\u0435)<\/p>\n<\/li>\n<li>\n<p>\u0441\u043b\u043e\u0436\u043d\u043e \u0440\u0435\u0430\u043b\u0438\u0437\u043e\u0432\u0430\u0442\u044c row-level \u0434\u043e\u0441\u0442\u0443\u043f<\/p>\n<\/li>\n<\/ul>\n<h2>\u0417\u0430\u043a\u043b\u044e\u0447\u0435\u043d\u0438\u0435 <\/h2>\n<p>\u0411\u0443\u043a\u0432\u0430\u043b\u044c\u043d\u043e \u043a\u0430\u0436\u0434\u044b\u0439 \u0433\u043e\u0434 \u043f\u043e\u044f\u0432\u043b\u044f\u044e\u0442\u0441\u044f \u043d\u043e\u0432\u044b\u0435 \u0438 \u043d\u043e\u0432\u044b\u0435 \u0444\u0440\u0435\u0439\u043c\u0432\u043e\u0440\u043a\u0438 \u043f\u043e \u0440\u0430\u0431\u043e\u0442\u0435 \u0441 \u0434\u0430\u043d\u043d\u044b\u043c\u0438, \u0432 \u0442\u043e\u043c \u0447\u0438\u0441\u043b\u0435 \u043f\u043e \u0432\u0438\u0437\u0443\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u0438, \u0441\u043d\u0438\u0436\u0430\u044f \u043f\u043e\u0440\u043e\u0433 \u0432\u0445\u043e\u0434\u0430 \u0430\u043d\u0430\u043b\u0438\u0442\u0438\u043a\u0430\u043c \u0438 \u0438\u043d\u0436\u0435\u043d\u0435\u0440\u0430\u043c. \u0420\u0430\u0437\u043c\u044b\u0432\u0430\u0435\u0442\u0441\u044f \u0433\u043b\u0430\u0432\u043d\u043e\u0435 \u043f\u0440\u0435\u0438\u043c\u0443\u0449\u0435\u0441\u0442\u0432\u043e \u0441\u0442\u0430\u043d\u0434\u0430\u0440\u0442\u043d\u044b\u0445 BI-\u0438\u043d\u0441\u0442\u0440\u0443\u043c\u0435\u043d\u0442\u043e\u0432 \u2013 \u043f\u0440\u043e\u0441\u0442\u043e\u0442\u0430 \u043f\u043e\u0441\u0442\u0440\u043e\u0435\u043d\u0438\u044f \u0434\u0430\u0448\u0431\u043e\u0440\u0434\u0430 (\u043f\u0440\u0430\u0432\u0434\u0430, \u0443\u0441\u043b\u043e\u0432\u043d\u0430\u044f). \u041f\u0440\u0435\u0438\u043c\u0443\u0449\u0435\u0441\u0442\u0432\u043e, \u0431\u0443\u0434\u0442\u043e \u043b\u044e\u0431\u043e\u0439 \u0431\u0438\u0437\u043d\u0435\u0441-\u0430\u043d\u0430\u043b\u0438\u0442\u0438\u043a \u0438\u043b\u0438 \u0434\u0430\u0436\u0435 \u043c\u0435\u043d\u0435\u0434\u0436\u0435\u0440 \u043c\u043e\u0433\u0443\u0442 \u043d\u0430\u0431\u0440\u043e\u0441\u0430\u0442\u044c \u043d\u0435\u0441\u043a\u043e\u043b\u044c\u043a\u043e \u0433\u0440\u0430\u0444\u0438\u043a\u043e\u0432, \u0445\u043e\u0442\u044f \u0447\u0430\u0449\u0435 \u0432\u0441\u0435\u0433\u043e \u0438\u043c\u0435\u043d\u043d\u043e data-\u0430\u043d\u0430\u043b\u0438\u0442\u0438\u043a\u0438 \u0434\u0435\u043b\u0430\u044e\u0442 \u044d\u0442\u043e. <\/p>\n<p>\u041d\u043e data-\u0430\u043d\u0430\u043b\u0438\u0442\u0438\u043a\u0438 \u0441\u0435\u0439\u0447\u0430\u0441 \u0432\u0441\u0435 \u0447\u0430\u0449\u0435 \u0438\u043c\u0435\u044e\u0442 \u044d\u043a\u0441\u043f\u0435\u0440\u0442\u0438\u0437\u0443 \u0432 python. \u0418 \u0432\u0441\u0435 \u0447\u0430\u0449\u0435 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u044e\u0442 pandas \u0438\u043b\u0438 \u0434\u0440\u0443\u0433\u0438\u0435 \u0444\u0440\u0435\u0439\u043c\u0432\u043e\u0440\u043a\u0438. \u041f\u0435\u0440\u0435\u0441\u0435\u0441\u0442\u044c \u0441 \u0433\u0440\u043e\u043c\u043e\u0437\u0434\u043a\u043e\u0433\u043e \u043c\u043e\u043d\u043e\u043b\u0438\u0442\u0430 \u0441 \u043d\u0435\u0431\u043e\u043b\u044c\u0448\u0438\u043c\u0438 \u0432\u043e\u0437\u043c\u043e\u0436\u043d\u043e\u0441\u0442\u044f\u043c\u0438 \u043a\u0430\u0441\u0442\u043e\u043c\u0438\u0437\u0430\u0446\u0438\u0438 \u043d\u0430 \u0432\u0438\u0437\u0443\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u044e \u0432 \u0442\u043e\u043c \u0436\u0435 python \u043e\u043d\u0438 \u0441\u043c\u043e\u0433\u0443\u0442. \u041f\u0440\u0438 \u044d\u0442\u043e\u043c, \u0441\u043c\u043e\u0433\u0443\u0442 \u0434\u0435\u043b\u0430\u0442\u044c \u0447\u0443\u0442\u044c \u043b\u0438 \u043d\u0435 \u043f\u043e\u043b\u043d\u043e\u0446\u0435\u043d\u043d\u044b\u0435 \u0438\u043d\u0442\u0435\u0440\u0444\u0435\u0439\u0441\u044b, \u043f\u0440\u0435\u0432\u0440\u0430\u0449\u0430\u044f \u0434\u0430\u0448\u0431\u043e\u0440\u0434\u044b \u0432 \u043a\u043e\u0441\u043c\u043e\u043b\u0435\u0442\u044b. <\/p>\n<p>\u0418 \u0442\u043e\u0433\u0434\u0430 \u0432\u0441\u0442\u0430\u0435\u0442 \u0433\u043b\u0430\u0432\u043d\u044b\u0439 \u0432\u043e\u043f\u0440\u043e\u0441 \u2013 \u0437\u0430\u0447\u0435\u043c \u043a\u043e\u043c\u043f\u0430\u043d\u0438\u0438 \u0442\u0440\u0430\u0442\u0438\u0442\u044c \u0443\u0439\u043c\u0443 \u0434\u0435\u043d\u0435\u0433 \u043d\u0430 \u043b\u0438\u0446\u0435\u043d\u0437\u0438\u0438 \u043f\u0440\u043e\u043f\u0440\u0438\u0435\u0442\u0430\u0440\u043d\u043e\u0433\u043e \u0440\u0435\u0448\u0435\u043d\u0438\u044f, \u0440\u0438\u0441\u043a\u0443\u044f \u043f\u0440\u0438 \u044d\u0442\u043e\u043c \u043f\u043e\u043b\u0443\u0447\u0438\u0442\u044c vendor-lock? <\/p>\n<p>\u041f\u0440\u0438\u0434\u0435\u043c \u043b\u0438 \u043c\u044b \u043a \u0442\u043e\u043c\u0443 \u0436\u0435, \u043a \u0447\u0435\u043c\u0443 \u043f\u0440\u0438\u0448\u043b\u0438 \u0432 \u043f\u0440\u043e\u0442\u0438\u0432\u043e\u0441\u0442\u043e\u044f\u043d\u0438\u0438 GUI ETL \u0438 ETL as a code? \u0415\u0441\u0442\u044c \u0432\u0441\u0435 \u043e\u0441\u043d\u043e\u0432\u0430\u043d\u0438\u044f \u043f\u043e\u043b\u0430\u0433\u0430\u0442\u044c, \u0447\u0442\u043e \u0434\u0430.<\/p>\n<\/div>\n<\/div>\n<\/div>\n<p><!----><!----><\/div>\n<p><!----><!----><br \/> \u0441\u0441\u044b\u043b\u043a\u0430 \u043d\u0430 \u043e\u0440\u0438\u0433\u0438\u043d\u0430\u043b \u0441\u0442\u0430\u0442\u044c\u0438 <a href=\"https:\/\/habr.com\/ru\/articles\/905156\/\"> https:\/\/habr.com\/ru\/articles\/905156\/<\/a><\/p>\n","protected":false},"excerpt":{"rendered":"<div><!--[--><!--]--><\/div>\n<div id=\"post-content-body\">\n<div>\n<div class=\"article-formatted-body article-formatted-body article-formatted-body_version-2\">\n<div xmlns=\"http:\/\/www.w3.org\/1999\/xhtml\">\n<p>\u0412\u0441\u0435\u0445 \u043f\u0440\u0438\u0432\u0435\u0442\u0441\u0442\u0432\u0443\u044e! \u0417\u043e\u0432\u0443\u0442 \u043c\u0435\u043d\u044f \u041f\u0430\u0432\u0435\u043b, \u0440\u0430\u0431\u043e\u0442\u0430\u044e \u0432 Datapulse. \u0421\u043e\u0437\u0434\u0430\u0435\u043c \u0440\u0430\u0437\u043b\u0438\u0447\u043d\u044b\u0435 \u0440\u0435\u0448\u0435\u043d\u0438\u044f \u0434\u043b\u044f \u0430\u0432\u0442\u043e\u043c\u0430\u0442\u0438\u0437\u0430\u0446\u0438\u0438 DWH.<\/p>\n<p>\u041c\u044b \u0436\u0438\u0432\u0435\u043c \u0432 \u043f\u0435\u0440\u0435\u0445\u043e\u0434\u043d\u043e\u043c \u043f\u0435\u0440\u0438\u043e\u0434\u0435, \u043a\u043e\u0433\u0434\u0430 \u043d\u0430 \u0441\u043c\u0435\u043d\u0443 \u0442\u0440\u0430\u0434\u0438\u0446\u0438\u043e\u043d\u043d\u044b\u043c \u0438\u043d\u0441\u0442\u0440\u0443\u043c\u0435\u043d\u0442\u0430\u043c \u0438 \u043f\u043e\u0434\u0445\u043e\u0434\u0430\u043c \u0432 data-engineer \u043f\u0440\u0438\u0445\u043e\u0434\u044f\u0442 \u043d\u043e\u0432\u044b\u0435. \u0422\u043e, \u0447\u0442\u043e \u0435\u0449\u0435 \u0432\u0447\u0435\u0440\u0430 \u043a\u0430\u0437\u0430\u043b\u043e\u0441\u044c \u0441\u0442\u0430\u043d\u0434\u0430\u0440\u0442\u043e\u043c \u0434\u0435-\u0444\u0430\u043a\u0442\u043e, \u0441\u0435\u0433\u043e\u0434\u043d\u044f \u0432\u0441\u0435 \u0447\u0430\u0449\u0435 \u0432\u043e\u0441\u043f\u0440\u0438\u043d\u0438\u043c\u0430\u0435\u0442\u0441\u044f \u043a\u0430\u043a \u0430\u043d\u0430\u0445\u0440\u043e\u043d\u0438\u0437\u043c. \u041f\u0440\u0438\u043c\u0435\u0440 \u0442\u043e\u043c\u0443 &#8212; \u0432\u043e\u0439\u043d\u0430 GUI (\u00ab\u0433\u0443\u0435\u0432\u044b\u0445\u00bb) ETL \u0438\u043d\u0441\u0442\u0440\u0443\u043c\u0435\u043d\u0442\u043e\u0432 \u0441\u043e \u0441\u043a\u0440\u0438\u043f\u0442\u043e\u0432\u044b\u043c\u0438 \u0437\u0430\u043a\u043e\u043d\u0447\u0438\u043b\u0430\u0441\u044c \u0431\u0435\u0437\u043e\u0433\u043e\u0432\u043e\u0440\u043e\u0447\u043d\u043e\u0439 \u043f\u043e\u0431\u0435\u0434\u043e\u0439 \u043f\u043e\u0441\u043b\u0435\u0434\u043d\u0438\u0445 (\u0432\u0441\u0435 \u0441\u043a\u0430\u0437\u0430\u043d\u043d\u043e\u0435 \u0437\u0434\u0435\u0441\u044c \u044f\u0432\u043b\u044f\u0435\u0442\u0441\u044f \u043c\u043d\u0435\u043d\u0438\u0435\u043c \u0430\u0432\u0442\u043e\u0440\u0430 \u0438 \u043d\u0435 \u043f\u0440\u0435\u0442\u0435\u043d\u0434\u0443\u0435\u0442 \u043d\u0430 \u0438\u0441\u0442\u0438\u043d\u0443 \u0432 \u043f\u043e\u0441\u043b\u0435\u0434\u043d\u0435\u0439 \u0438\u043d\u0441\u0442\u0430\u043d\u0446\u0438\u0438). \u0421\u043a\u0440\u0438\u043f\u0442\u044b \u0432\u0437\u044f\u043b\u0438 \u0432\u0435\u0440\u0445 \u0431\u043b\u0430\u0433\u043e\u0434\u0430\u0440\u044f \u0441\u0432\u043e\u0435\u0439 \u043c\u0430\u0441\u0448\u0442\u0430\u0431\u0438\u0440\u0443\u0435\u043c\u043e\u0441\u0442\u0438, \u0432\u043e\u0437\u043c\u043e\u0436\u043d\u043e\u0441\u0442\u0438 \u043f\u0440\u0438\u043c\u0435\u043d\u0435\u043d\u0438\u044f Git \u0438 \u043b\u0443\u0447\u0448\u0435\u0439 \u0438\u043d\u0442\u0435\u0433\u0440\u0430\u0446\u0438\u0438 \u0432 \u043f\u0440\u043e\u0446\u0435\u0441\u0441\u044b CI\/CD. <\/p>\n<p>\u0410 \u0447\u0442\u043e \u0436\u0435 \u0441 BI? \u041c\u044b \u043f\u0440\u0438\u0432\u044b\u043a\u043b\u0438 \u043a \u043c\u0430\u0441\u0442\u043e\u0434\u043e\u043d\u0442\u0430\u043c: PowerBI, Tableau, Qlik. \u041b\u0438\u0431\u043e open-source: Superset, Metabase. \u041e\u043d\u0438 \u0432 \u0441\u0432\u043e\u0435 \u0432\u0440\u0435\u043c\u044f \u0441\u0442\u0430\u043b\u0438 \u0441\u0442\u0430\u043d\u0434\u0430\u0440\u0442\u043e\u043c, \u0437\u0430\u0445\u0432\u0430\u0442\u0438\u0432 \u0431\u043e\u043b\u044c\u0448\u0443\u044e \u0447\u0430\u0441\u0442\u044c \u0440\u044b\u043d\u043a\u0430, \u0430 sales manager \u043f\u043e\u043b\u0443\u0447\u0430\u043b\u0438 \u043e\u0447\u0435\u043d\u044c \u043d\u0435\u043f\u043b\u043e\u0445\u0438\u0435 \u043f\u0440\u0435\u043c\u0438\u0438 \u0437\u0430 \u0438\u0445 \u0432\u043d\u0435\u0434\u0440\u0435\u043d\u0438\u0435. \u0418, \u043a\u0430\u0437\u0430\u043b\u043e\u0441\u044c, \u0432\u0440\u0435\u043c\u044f \u043c\u043e\u043b\u043e\u0447\u043d\u044b\u0445 \u0440\u0435\u043a \u0438 \u043a\u0438\u0441\u0435\u043b\u044c\u043d\u044b\u0445 \u0431\u0435\u0440\u0435\u0433\u043e\u0432 \u043d\u0435 \u0437\u0430\u043a\u043e\u043d\u0447\u0438\u0442\u0441\u044f \u043d\u0438\u043a\u043e\u0433\u0434\u0430. \u041a\u043e\u043c\u043f\u0430\u043d\u0438\u0438 \u043f\u0440\u043e\u0434\u043e\u043b\u0436\u0430\u0442 \u0442\u0440\u0430\u0442\u0438\u0442\u044c \u043a\u0443\u0447\u0443 \u0434\u0435\u043d\u0435\u0433 \u043d\u0430 \u043a\u0440\u0430\u0441\u0438\u0432\u0435\u043d\u044c\u043a\u0438\u0435 \u0433\u0440\u0430\u0444\u0438\u043a\u0438, \u043a\u043e\u0442\u043e\u0440\u044b\u043c\u0438 \u043d\u0435 \u043f\u043e\u043b\u044c\u0437\u0443\u044e\u0442\u0441\u044f, \u0430 \u0432 \u0441\u043e\u043e\u0431\u0449\u0435\u0441\u0442\u0432\u0435 \u0430\u043d\u0430\u043b\u0438\u0442\u0438\u043a\u043e\u0432 \u0431\u0443\u0434\u0443\u0442 \u043b\u0438\u0448\u044c \u0440\u0430\u0441\u0441\u0443\u0436\u0434\u0430\u0442\u044c \u043e \u0442\u043e\u043c, \u043a\u0430\u043a \u0434\u043e\u043a\u0430\u0437\u0430\u0442\u044c \u0443\u043f\u0435\u0440\u0442\u043e\u043c\u0443 \u043c\u0435\u043d\u0435\u0434\u0436\u0435\u0440\u0443, \u0447\u0442\u043e \u0434\u0430\u0448\u0431\u043e\u0440\u0434-\u0432\u0443\u043d\u0434\u0435\u0440\u0432\u0430\u0444\u043b\u044f \u0432 PowerBI \u0433\u043e\u0440\u0430\u0437\u0434\u043e \u0443\u0434\u043e\u0431\u043d\u0435\u0435 \u0441\u0442\u0430\u0440\u043e\u0433\u043e \u0434\u043e\u0431\u0440\u043e\u0433\u043e Excel (\u0448\u0443\u0442\u043a\u0430).<\/p>\n<p>\u0410 \u0432\u0440\u0435\u043c\u0435\u043d\u0430 \u044d\u0442\u0438, \u0435\u0441\u043b\u0438 \u0438 \u043d\u0435 \u043f\u0440\u043e\u0448\u043b\u0438, \u0442\u043e \u0431\u043b\u0438\u0437\u044f\u0442\u0441\u044f \u043a \u0437\u0430\u043a\u0430\u0442\u0443. \u0421\u0442\u0430\u0440\u044b\u0435 \u043a\u043e\u0440\u043e\u043b\u0438 \u0447\u0430\u0445\u043d\u0443\u0442 \u0438 \u0443\u043c\u0438\u0440\u0430\u044e\u0442. \u041f\u0440\u0438\u0432\u0435\u0442\u0441\u0442\u0432\u0443\u0435\u043c \u043d\u043e\u0432\u044b\u0445! <\/p>\n<figure class=\"full-width\"><\/figure>\n<p>\u0427\u0435\u043c \u043f\u043b\u043e\u0445\u0438 \u0441\u0442\u0430\u043d\u0434\u0430\u0440\u0442\u043d\u044b\u0435 BI-\u0438\u043d\u0441\u0442\u0440\u0443\u043c\u0435\u043d\u0442\u044b? \u041f\u043e\u0441\u043c\u0435\u044e \u043f\u0435\u0440\u0435\u0447\u0438\u0441\u043b\u0438\u0442\u044c \u0438\u0445 \u043d\u0435\u0434\u043e\u0441\u0442\u0430\u0442\u043a\u0438:<\/p>\n<ul>\n<li>\n<p>\u0441\u043b\u043e\u0436\u043d\u043e\u0435 \u0432\u043d\u0435\u0434\u0440\u0435\u043d\u0438\u0435 \u0438 \u043e\u0431\u0441\u043b\u0443\u0436\u0438\u0432\u0430\u043d\u0438\u0435<\/p>\n<\/li>\n<li>\n<p>\u043c\u0438\u043d\u0438\u043c\u0430\u043b\u044c\u043d\u044b\u0435 \u0432\u043e\u0437\u043c\u043e\u0436\u043d\u043e\u0441\u0442\u0438 \u0438\u043d\u0442\u0435\u0433\u0440\u0430\u0446\u0438\u0438 \u0441 Git (\u0438\u043b\u0438 \u0432\u043e\u043e\u0431\u0449\u0435 \u0438\u0445 \u043e\u0442\u0441\u0443\u0442\u0441\u0442\u0432\u0438\u0435)<\/p>\n<\/li>\n<li>\n<p>\u0441\u043b\u0430\u0431\u044b\u0435 \u0432\u043e\u0437\u043c\u043e\u0436\u043d\u043e\u0441\u0442\u0438 \u043a\u0430\u0441\u0442\u043e\u043c\u0438\u0437\u0430\u0446\u0438\u0438 (\u043f\u043e\u0442\u0440\u0435\u0431\u0443\u0435\u0442\u0441\u044f \u043d\u0435\u0441\u043b\u0430\u0431\u0430\u044f \u044d\u043a\u0441\u043f\u0435\u0440\u0442\u0438\u0437\u0430)<\/p>\n<\/li>\n<li>\n<p>\u0432\u044b\u0441\u043e\u043a\u0430\u044f\/\u043e\u0447\u0435\u043d\u044c \u0432\u044b\u0441\u043e\u043a\u0430\u044f \u0441\u0442\u043e\u0438\u043c\u043e\u0441\u0442\u044c (\u0435\u0441\u043b\u0438 \u043f\u0440\u043e\u043f\u0440\u0438\u0435\u0442\u0430\u0440\u043d\u043e\u0435 \u041f\u041e)<\/p>\n<\/li>\n<li>\n<p>\u0441\u043b\u043e\u0436\u043d\u043e \u0440\u0430\u0437\u043e\u0431\u0440\u0430\u0442\u044c\u0441\u044f \u0438 \u043f\u043e\u0434\u0434\u0435\u0440\u0436\u0438\u0432\u0430\u0442\u044c (\u0435\u0441\u043b\u0438 open-source)<\/p>\n<\/li>\n<\/ul>\n<blockquote>\n<p>&#8212; \u0414\u043e\u0432\u043e\u043b\u044c\u043d\u043e \u043f\u0440\u0435\u0434\u0438\u0441\u043b\u043e\u0432\u0438\u044f! \u0414\u0430\u0436\u0435 \u0435\u0441\u043b\u0438 \u044f \u0441\u043e\u0433\u043b\u0430\u0448\u0443\u0441\u044c \u0441\u043e \u0441\u043a\u0430\u0437\u0430\u043d\u043d\u044b\u043c, \u0447\u0435\u043c \u0437\u0430\u043c\u0435\u043d\u0438\u0442\u044c \u0441\u0442\u0430\u043d\u0434\u0430\u0440\u0442\u043d\u044b\u0439 BI? \u2013 \u0441\u043f\u0440\u043e\u0441\u0438\u0442 \u0447\u0438\u0442\u0430\u0442\u0435\u043b\u044c. <br \/>&#8212; \u0423 \u043c\u0435\u043d\u044f \u0435\u0441\u0442\u044c \u0434\u043b\u044f \u0432\u0430\u0441 \u043f\u0430\u0440\u0430 \u0431\u043b\u044e\u0434! \u2013 \u043e\u0442\u0432\u0435\u0447\u0443 \u044f.<\/p>\n<\/blockquote>\n<p>\u0421\u043a\u0440\u0438\u043f\u0442\u043e\u0432\u044b\u0435 BI \u0438\u043b\u0438 BI as a code. \u041f\u043e\u044f\u0432\u0438\u043b\u0438\u0441\u044c \u043d\u0435 \u0442\u0430\u043a \u0434\u0430\u0432\u043d\u043e, \u043d\u043e \u0443\u0436\u0435 \u043d\u0430\u0431\u0438\u0440\u0430\u044e\u0442 \u0431\u0435\u0448\u0435\u043d\u043d\u0443\u044e \u043f\u043e\u043f\u0443\u043b\u044f\u0440\u043d\u043e\u0441\u0442\u044c. \u0421\u0440\u0430\u0432\u043d\u0438\u0432\u0430\u0442\u044c \u0440\u0430\u0437\u043b\u0438\u0447\u043d\u044b\u0435 \u0440\u0435\u0448\u0435\u043d\u0438\u044f \u0432 \u044d\u0442\u043e\u0439 \u0441\u0442\u0430\u0442\u044c\u044f \u044f \u043d\u0435 \u0431\u0443\u0434\u0443. \u0415\u0441\u0442\u044c \u0437\u0430\u043c\u0435\u0447\u0430\u0442\u0435\u043b\u044c\u043d\u0430\u044f \u043e\u0431\u0437\u043e\u0440\u043d\u0430\u044f <a href=\"https:\/\/motherduck.com\/blog\/the-future-of-bi-bi-as-code-duckdb-impact\/\" rel=\"noopener noreferrer nofollow\">\u0441\u0442\u0430\u0442\u044c\u044f<\/a>, \u0433\u0434\u0435 \u043e\u043f\u0438\u0441\u044b\u0432\u0430\u044e\u0442\u0441\u044f \u043d\u0430\u0438\u0431\u043e\u043b\u0435\u0435 \u043f\u043e\u043f\u0443\u043b\u044f\u0440\u043d\u044b\u0435 \u0444\u0440\u0435\u0439\u043c\u0432\u043e\u0440\u043a\u0438. \u0412\u0441\u0435 \u043f\u0440\u0438\u043c\u0435\u0440\u044b \u0441\u043a\u0440\u0438\u043f\u0442\u043e\u0432\u043e\u0433\u043e BI \u0432 \u044d\u0442\u043e\u0439 \u0441\u0442\u0430\u0442\u044c\u0435 \u0431\u0443\u0434\u0443\u0442 \u043e\u0441\u043d\u043e\u0432\u0430\u043d\u044b \u043d\u0430 \u0444\u0440\u0435\u0439\u043c\u0432\u043e\u0440\u043a\u0435 <code>streamlit<\/code>, \u043f\u043e\u0442\u043e\u043c\u0443 \u0447\u0442\u043e \u0430\u0432\u0442\u043e\u0440 \u0431\u043e\u043b\u0435\u0435 \u0447\u0435\u043c \u0445\u043e\u0440\u043e\u0448\u043e \u0441 \u043d\u0438\u043c \u0437\u043d\u0430\u043a\u043e\u043c.<\/p>\n<p>\u0421\u043f\u0435\u0440\u0432\u0430 \u043e\u043f\u0440\u0435\u0434\u0435\u043b\u0438\u043c \u044f\u0432\u043d\u044b\u0435 \u0434\u043e\u0441\u0442\u043e\u0438\u043d\u0441\u0442\u0432\u0430 \u0441\u043a\u0440\u0438\u043f\u0442\u043e\u0432\u043e\u0433\u043e BI \u0432 \u043e\u0442\u043d\u043e\u0448\u0435\u043d\u0438\u0438 \u0441\u0442\u0430\u043d\u0434\u0430\u0440\u0442\u043d\u043e\u0433\u043e BI: <\/p>\n<ul>\n<li>\n<p>\u042d\u043b\u0435\u043c\u0435\u043d\u0442\u0430\u0440\u043d\u0430\u044f \u0443\u0441\u0442\u0430\u043d\u043e\u0432\u043a\u0430 \u0432 \u043f\u0430\u0440\u0443 \u043a\u043e\u043c\u0430\u043d\u0434<\/p>\n<\/li>\n<li>\n<p>\u041f\u0440\u043e\u0441\u0442\u043e\u0439 \u043e\u0431\u043c\u0435\u043d \u0434\u0430\u0448\u0431\u043e\u0440\u0434\u0430\u043c\u0438 (\u043e\u0431\u043c\u0435\u043d\u0438\u0432\u0430\u0435\u0442\u0435\u0441\u044c \u043b\u0438\u0448\u044c \u0444\u0430\u0439\u043b\u0430\u043c\u0438)<\/p>\n<\/li>\n<li>\n<p>\u0410\u043f\u0440\u0438\u043e\u0440\u0438 \u0434\u0440\u0443\u0436\u0438\u0442 \u0441 Git<\/p>\n<\/li>\n<li>\n<p>\u0411\u0435\u0437\u0433\u0440\u0430\u043d\u0438\u0447\u043d\u044b\u0435 \u0432\u043e\u0437\u043c\u043e\u0436\u043d\u043e\u0441\u0442\u0438 \u043a\u0430\u0441\u0442\u043e\u043c\u0438\u0437\u0430\u0446\u0438\u0438 (\u043c\u043e\u0436\u043d\u043e \u0440\u0435\u0430\u043b\u0438\u0437\u043e\u0432\u0430\u0442\u044c \u043f\u043e\u043b\u043d\u043e\u0446\u0435\u043d\u043d\u044b\u0439 \u0438\u043d\u0442\u0435\u0440\u0444\u0435\u0439\u0441!)<\/p>\n<\/li>\n<li>\n<p>\u0411\u0435\u0441\u043f\u043b\u0430\u0442\u043d\u043e (\u0443\u0441\u043b\u043e\u0432\u043d\u043e)<\/p>\n<\/li>\n<\/ul>\n<h2>\u041f\u0440\u0430\u043a\u0442\u0438\u0447\u0435\u0441\u043a\u043e\u0435 \u043f\u0440\u0438\u043c\u0435\u043d\u0435\u043d\u0438\u0435<\/h2>\n<p>\u041f\u0435\u0440\u0435\u0439\u0434\u0435\u043c \u0436\u0435 \u043a \u043d\u0435\u043f\u043e\u0441\u0440\u0435\u0434\u0441\u0442\u0432\u0435\u043d\u043d\u044b\u043c \u043f\u0440\u0438\u043c\u0435\u0440\u0430\u043c.<\/p>\n<p>\u0423\u0441\u0442\u0430\u043d\u043e\u0432\u043a\u0430 \u0431\u043e\u043b\u0435\u0435 \u0447\u0435\u043c \u043f\u0440\u043e\u0441\u0442\u0430\u044f.<\/p>\n<pre><code class=\"bash\">pip install streamlit<\/code><\/pre>\n<p>\u041f\u0438\u0448\u0435\u043c \u044d\u043b\u0435\u043c\u0435\u043d\u0442\u0430\u0440\u043d\u044b\u0439 \u043a\u043e\u0434 \u0434\u043b\u044f \u0433\u0440\u0430\u0444\u0438\u043a\u0430, \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u0447\u0438\u0442\u0430\u0435\u0442 \u0432\u0438\u0442\u0440\u0438\u043d\u0443 \u0441 \u0437\u0430\u043a\u0430\u0437\u0430\u043c\u0438 \u0438\u0437 PostgreSQL \u0438 \u0432\u0435\u0440\u0441\u0442\u0430\u0435\u0442 \u0433\u0440\u0430\u0444\u0438\u043a.<\/p>\n<pre><code class=\"python\">from sqlalchemy import create_engine import pandas as pd import streamlit as st  # \u0421\u043e\u0437\u0434\u0430\u0435\u043c \u0434\u0432\u0438\u0436\u043e\u043a \u043f\u043e\u0434\u043a\u043b\u044e\u0447\u0435\u043d\u0438\u044f engine = create_engine('postgresql+psycopg2:\/\/postgres:admin@localhost:5432\/database')  # \u0412\u044b\u043f\u043e\u043b\u043d\u044f\u0435\u043c \u0437\u0430\u043f\u0440\u043e\u0441 query = \"\"\" select     order_month,     sum(total_orders) as orders_count from dm.mart_order group by     order_month \"\"\" df = pd.read_sql_query(query, engine) # \u0413\u0440\u0430\u0444\u0438\u043a st.bar_chart(df, x=\"order_month\", y=\"orders_count\", x_label=\"\u041c\u0435\u0441\u044f\u0446\", y_label=\"\u041a\u043e\u043b-\u0432\u043e \u0437\u0430\u043a\u0430\u0437\u043e\u0432\")<\/code><\/pre>\n<p>\u0417\u0430\u043f\u0443\u0441\u043a\u0430\u0435\u043c streamlit \u0432 \u0431\u0440\u0430\u0443\u0437\u0435\u0440\u0435<\/p>\n<pre><code class=\"bash\">streamlit run app.py<\/code><\/pre>\n<figure class=\"full-width\">\n<div><figcaption>\u0420\u0435\u0437\u0443\u043b\u044c\u0442\u0430\u0442<\/figcaption><\/div>\n<\/figure>\n<p>\u041a\u043e\u043d\u0435\u0447\u043d\u043e, \u044d\u043b\u0435\u043c\u0435\u043d\u0442\u0430\u0440\u043d\u044b\u0439 bar chart \u2013 \u0441\u043b\u0438\u0448\u043a\u043e\u043c \u0441\u043a\u0443\u0447\u043d\u043e. \u0414\u0430\u0432\u0430\u0439\u0442\u0435 \u0434\u043e\u0431\u0430\u0432\u0438\u043c \u0441\u044e\u0434\u0430 \u0444\u0438\u043b\u044c\u0442\u0440\u044b \u043f\u043e \u043f\u0435\u0440\u0438\u043e\u0434\u0443 \u0438 \u043f\u0440\u043e\u0434\u0443\u043a\u0442\u0443. \u041e\u0431\u0440\u0430\u0449\u0430\u044e \u0432\u043d\u0438\u043c\u0430\u043d\u0438\u0435, \u0447\u0442\u043e \u0438\u0437 \u043a\u043e\u0440\u043e\u0431\u043a\u0438 <code>streamlit<\/code> \u0443\u043c\u0435\u0435\u0442 \u043a\u044d\u0448\u0438\u0440\u043e\u0432\u0430\u0442\u044c \u0434\u0430\u043d\u043d\u044b\u0435, \u0447\u0442\u043e\u0431\u044b \u0432 \u043d\u0430\u0448\u0435\u043c \u0441\u043b\u0443\u0447\u0430\u0435 \u043d\u0435 \u0432\u044b\u043f\u043e\u043b\u043d\u044f\u0442\u044c \u0442\u044f\u0436\u0435\u043b\u044b\u0435 SQL \u043f\u043e \u043d\u0435\u0441\u043a\u043e\u043b\u044c\u043a\u0443 \u0440\u0430\u0437.<\/p>\n<details class=\"spoiler\">\n<summary>\u0418\u0437\u043c\u0435\u043d\u0435\u043d\u043d\u044b\u0439 python-\u0441\u043a\u0440\u0438\u043f\u0442<\/summary>\n<div class=\"spoiler__content\">\n<pre><code class=\"python\">from sqlalchemy import create_engine import pandas as pd import streamlit as st  # \u0421\u043e\u0437\u0434\u0430\u0435\u043c \u0434\u0432\u0438\u0436\u043e\u043a \u043f\u043e\u0434\u043a\u043b\u044e\u0447\u0435\u043d\u0438\u044f engine = create_engine('postgresql+psycopg2:\/\/postgres:admin@localhost:5432\/databaase')  # \u043f\u043e\u043b\u0443\u0447\u0430\u0435\u043c \u0438 \u043a\u044d\u0448\u0438\u0440\u0443\u0435\u043c \u0434\u0430\u043d\u043d\u044b\u0435 spinner_text = \"\u0427\u0442\u0435\u043d\u0438\u0435 \u0434\u0430\u043d\u043d\u044b\u0445...\" @st.cache_data(ttl=300, max_entries=1000, show_spinner=spinner_text) def get_unique_values(column):     \"\"\"\u041f\u043e\u043b\u0443\u0447\u0430\u0435\u043c \u0443\u043d\u0438\u043a\u0430\u043b\u044c\u043d\u044b\u0435 \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u044f \u0441\u0442\u043e\u043b\u0431\u0446\u0430 \u0432\u0438\u0442\u0440\u0438\u043d\u044b\"\"\"     return  pd.read_sql_query(         f\"select distinct {column} from dm.mart_order order by {column}\",         engine)  @st.cache_data(ttl=300, max_entries=1000, show_spinner=spinner_text) def get_order_data(         date_from = None,         date_to = None,         city = None,         product = None ):     \"\"\"\u041f\u043e\u043b\u0443\u0447\u0430\u0435\u043c \u0434\u0430\u043d\u043d\u044b\u0435 \u0438\u0437 \u0432\u0438\u0442\u0440\u0438\u043d\u044b\"\"\"     filter = ''     if date_from:         filter += f\" and order_month&gt;='{str(date_from)}'\"     if date_to:         filter += f\" and order_month&lt;='{str(date_to)}'\"     if city:         city = \"','\".join(city)         filter += f\" and city_name in ('{city}')\"     if product:         product = \"','\".join(product)         filter += f\" and product_category in ('{str(product)}')\"     query = f\"\"\" select     order_month,     sum(total_orders) as orders_count from dm.mart_order where 1=1     {filter} group by     order_month \"\"\"     return pd.read_sql_query(query, engine)<\/code><\/pre>\n<\/div>\n<\/details>\n<figure class=\"full-width\">\n<div><figcaption>\u0420\u0435\u0437\u0443\u043b\u044c\u0442\u0430\u0442<\/figcaption><\/div>\n<\/figure>\n<p>\u041d\u0430\u0431\u043e\u0440 \u0432\u0438\u0437\u0443\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u0439 \u0432 <code>streamlit<\/code> \u0441\u043a\u0443\u0434\u043d\u044b\u0439, \u043d\u043e \u043c\u043e\u0436\u043d\u043e \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c \u0441\u0442\u043e\u0440\u043e\u043d\u043d\u0438\u0435 \u0431\u0438\u0431\u043b\u0438\u043e\u0442\u0435\u043a\u0438. \u0422\u0430\u043a\u0438\u0435 \u043a\u0430\u043a <code>plotly<\/code>, <code>altair<\/code>, <code>vega lite<\/code> \u0438 \u0434\u0440\u0443\u0433\u0438\u0435. \u041c\u043d\u0435 \u043b\u0438\u0447\u043d\u043e \u043f\u043e \u0434\u0443\u0448\u0435 <code>echarts<\/code>. \u0423 \u043d\u0435\u0433\u043e \u0431\u043e\u043b\u044c\u0448\u0438\u0435 \u0432\u043e\u0437\u043c\u043e\u0436\u043d\u043e\u0441\u0442\u0438, \u043d\u043e \u0434\u043e\u0441\u0442\u0430\u0442\u043e\u0447\u043d\u043e \u0441\u043b\u043e\u0436\u043d\u044b\u0439 \u0438\u043d\u0442\u0435\u0440\u0444\u0435\u0439\u0441 \u0432 \u0432\u0438\u0434\u0435 json-\u043f\u0430\u0440\u0430\u043c\u0435\u0442\u0440\u043e\u0432.<\/p>\n<pre><code class=\"bash\">pip install streamlit-echarts<\/code><\/pre>\n<p>Json-\u043f\u0430\u0440\u0430\u043c\u0435\u0442\u0440\u043e\u0432 \u043e\u0433\u0440\u043e\u043c\u043d\u043e\u0435 \u043a\u043e\u043b\u0438\u0447\u0435\u0441\u0442\u0432\u043e, \u0440\u0430\u0437\u043e\u0431\u0440\u0430\u0442\u044c\u0441\u044f \u0432 \u043d\u0438\u0445 \u0441 \u043d\u0443\u043b\u044f \u0441\u043b\u043e\u0436\u043d\u043e, \u043d\u043e \u043c\u043e\u0436\u043d\u043e \u0443\u043f\u0440\u043e\u0441\u0442\u0438\u0442\u044c \u0441\u0435\u0431\u0435 \u0436\u0438\u0437\u043d\u044c \u0438 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c ChatGPT.<\/p>\n<details class=\"spoiler\">\n<summary>\u041f\u0440\u0438\u043c\u0435\u0440 promt \u0438 \u043e\u0442\u0432\u0435\u0442\u0430<\/summary>\n<div class=\"spoiler__content\">\n<blockquote>\n<p>\u0415\u0441\u0442\u044c pandas.dataframe \u0441\u043e \u0441\u0442\u043e\u043b\u0431\u0446\u0430\u043c\u0438 <code>order_month<\/code>, <code>orders_count<\/code> \u0438 <code>average_order_amount<\/code>. \u0421\u0444\u043e\u0440\u043c\u0438\u0440\u0443\u0439 json \u0441 \u043f\u0430\u0440\u0430\u043c\u0435\u0442\u0440\u0430\u043c\u0438 \u0434\u043b\u044f \u0433\u0440\u0430\u0444\u0438\u043a\u0430 <code>streamlit_echarts<\/code> , \u043f\u043e \u043e\u0441\u0438 X <code>orders_month<\/code>, \u043f\u043e \u043e\u0441\u0438 Y <code>orders_count<\/code> \u0441 \u0442\u0438\u043f\u043e\u043c bar, \u0446\u0432\u0435\u0442 <code>orders_count<\/code> &#8212; <code>#1E90FF<\/code>. \u041f\u043e \u0434\u043e\u043f\u043e\u043b\u043d\u0438\u0442\u0435\u043b\u044c\u043d\u043e\u0439 \u043e\u0441\u0438 Y <code>average_order_amount<\/code> c \u0442\u0438\u043f\u043e\u043c line, \u0446\u0432\u0435\u0442 &#8212; <code>#B0E0E6<\/code>. \u0423 \u0433\u0440\u0430\u0444\u0438\u043a\u0430 \u0434\u043e\u043b\u0436\u0435\u043d \u0431\u044b\u0442\u044c title &#8212; \u0417\u0430\u043a\u0430\u0437\u044b \u043f\u043e \u043c\u0435\u0441\u044f\u0446\u0430\u043c.<\/p>\n<\/blockquote>\n<p>\u041e\u0442\u0432\u0435\u0442 ChatGPT<\/p>\n<pre><code class=\"json\">{     \"title\": {         \"text\": \"\u0417\u0430\u043a\u0430\u0437\u044b \u043f\u043e \u043c\u0435\u0441\u044f\u0446\u0430\u043c\",         \"left\": \"center\",         \"textStyle\": {             \"color\": \"#FFFFFF\"  # \u0426\u0432\u0435\u0442 \u0437\u0430\u0433\u043e\u043b\u043e\u0432\u043a\u0430         }     },     \"tooltip\": {         \"trigger\": \"axis\"     },     \"legend\": {         \"data\": [\"\u041a\u043e\u043b\u0438\u0447\u0435\u0441\u0442\u0432\u043e \u0437\u0430\u043a\u0430\u0437\u043e\u0432\", \"\u0421\u0440\u0435\u0434\u043d\u0438\u0439 \u0447\u0435\u043a\"],          \"bottom\": \"0%\",   # \u041b\u0435\u0433\u0435\u043d\u0434\u0430 \u0432\u043d\u0438\u0437\u0443          \"left\": \"center\", # \u0412\u044b\u0440\u0430\u0432\u043d\u0438\u0432\u0430\u043d\u0438\u0435 \u043f\u043e \u0446\u0435\u043d\u0442\u0440\u0443 \u043f\u043e \u0433\u043e\u0440\u0438\u0437\u043e\u043d\u0442\u0430\u043b\u0438          \"orient\": \"horizontal\",  # \u0413\u043e\u0440\u0438\u0437\u043e\u043d\u0442\u0430\u043b\u044c\u043d\u0430\u044f \u043e\u0440\u0438\u0435\u043d\u0442\u0430\u0446\u0438\u044f         \"textStyle\": {             \"color\": \"#FFFFFF\"  # \u0426\u0432\u0435\u0442 \u0437\u0430\u0433\u043e\u043b\u043e\u0432\u043a\u0430         }     },     \"xAxis\": {         \"type\": \"category\",         \"data\": df['order_month'].tolist()     },     \"yAxis\": [         {             \"type\": \"value\",             \"name\": \"\u041a\u043e\u043b\u0438\u0447\u0435\u0441\u0442\u0432\u043e \u0437\u0430\u043a\u0430\u0437\u043e\u0432\"         },         {             \"type\": \"value\",             \"name\": \"\u0421\u0440\u0435\u0434\u043d\u0438\u0439 \u0447\u0435\u043a\",             \"position\": \"right\"         }     ],     \"series\": [         {             \"name\": \"\u041a\u043e\u043b\u0438\u0447\u0435\u0441\u0442\u0432\u043e \u0437\u0430\u043a\u0430\u0437\u043e\u0432\",             \"type\": \"bar\",             \"data\": df['orders_count'].tolist(),             \"itemStyle\": {                 \"color\": \"#1E90FF\"             },             \"yAxisIndex\": 0         },         {             \"name\": \"\u0421\u0440\u0435\u0434\u043d\u0438\u0439 \u0447\u0435\u043a\",             \"type\": \"line\",             \"data\": df['average_order_amount'].tolist(),             \"itemStyle\": {                 \"color\": \"#B0E0E6\"             },             \"yAxisIndex\": 1         }     ] } <\/code><\/pre>\n<\/div>\n<\/details>\n<figure class=\"full-width\">\n<div><figcaption>\u041e\u0444\u043e\u0440\u043c\u043b\u0435\u043d\u0438\u0435 \u0433\u0440\u0430\u0444\u0438\u043a\u0430 \u0441 echarts<\/figcaption><\/div>\n<\/figure>\n<p>SQL-\u0437\u0430\u043f\u0440\u043e\u0441\u044b \u043b\u0443\u0447\u0448\u0435 \u0445\u0440\u0430\u043d\u0438\u0442\u044c \u043e\u0442\u0434\u0435\u043b\u044c\u043d\u043e \u0438 \u0432\u044b\u0437\u044b\u0432\u0430\u0442\u044c \u0438\u0445 \u043a\u0430\u043a jinja-\u0441\u043a\u0440\u0438\u043f\u0442\u044b. \u0418 \u043f\u0435\u0440\u0435\u043d\u0435\u0441\u0442\u0438 \u0442\u0443\u0434\u0430 \u0432\u0441\u044e \u043b\u043e\u0433\u0438\u043a\u0443 \u0444\u043e\u0440\u043c\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u044f SQL-\u0437\u0430\u043f\u0440\u043e\u0441\u0430 \u043d\u0430 \u043e\u0441\u043d\u043e\u0432\u0430\u043d\u0438\u0438 \u0444\u0438\u043b\u044c\u0442\u0440\u043e\u0432. \u0422\u0430\u043a\u0436\u0435 \u0431\u043e\u043b\u044c\u0448\u0438\u0435 json \u0441 \u043f\u0430\u0440\u0430\u043c\u0435\u0442\u0440\u0430\u043c\u0438 echarts-\u0433\u0440\u0430\u0444\u0438\u043a\u043e\u0432 \u0442\u043e\u0436\u0435 \u0432 \u043e\u0442\u0434\u0435\u043b\u044c\u043d\u044b\u0435 \u0444\u0430\u0439\u043b\u044b.<\/p>\n<details class=\"spoiler\">\n<summary>\u041f\u0440\u0438\u043c\u0435\u0440 jinja-\u0441\u043a\u0440\u0438\u043f\u0442\u0430<\/summary>\n<div class=\"spoiler__content\">\n<pre><code class=\"sql\">select     order_month,     sum(total_orders) as orders_count,     sum(total_order_amount)\/sum(total_orders) as average_order_amount from dm.mart_order where 1=1     {% if date_from %}and order_month&gt;='{{ date_from }}'{% endif %}     {% if date_to %}and order_month&lt;='{{ date_to }}'{% endif %}     {% if city %}and city_name in ({% for item in city %}'{{ item }}'{% if not loop.last %},{% endif %}{% endfor %}){% endif %}     {% if product %}and product_category in ({% for item in product %}'{{ item }}'{% if not loop.last %},{% endif %}{% endfor %}){% endif %} group by     order_month<\/code><\/pre>\n<\/div>\n<\/details>\n<details class=\"spoiler\">\n<summary>\u041f\u0435\u0440\u0435\u043f\u0438\u0441\u0430\u043d\u043d\u044b\u0439 python-\u0441\u043a\u0440\u0438\u043f\u0442 \u0441 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u043d\u0438\u0435\u043c jinja<\/summary>\n<div class=\"spoiler__content\">\n<pre><code class=\"python\">@st.cache_data(ttl=300, max_entries=1000, show_spinner=spinner_text) def get_order_data(         date_from = None,         date_to = None,         city = None,         product = None ):     \"\"\"\u041f\u043e\u043b\u0443\u0447\u0430\u0435\u043c \u0434\u0430\u043d\u043d\u044b\u0435 \u0438\u0437 \u0432\u0438\u0442\u0440\u0438\u043d\u044b\"\"\"     with open('get_order_data.sql', \"r\", encoding=\"utf-8\") as f:         file = f.read()     script = jinja2.Template(file).render(         date_from=str(date_from) if date_from else None,         date_to=str(date_to) if date_to else None,         city=city,         product=product     )     st.write(script)     return pd.read_sql_query(script, engine)<\/code><\/pre>\n<\/div>\n<\/details>\n<p>\u0414\u043e\u0431\u0430\u0432\u0438\u043c \u043d\u0430 \u0434\u0430\u0448\u0431\u043e\u0440\u0434 \u0435\u0449\u0435 \u0433\u0440\u0430\u0444\u0438\u043a\u043e\u0432 \u0434\u043b\u044f \u0431\u043e\u043b\u0435\u0435 \u043a\u0440\u0430\u0441\u043e\u0447\u043d\u043e\u0433\u043e \u043f\u0440\u0438\u043c\u0435\u0440\u0430.<\/p>\n<figure class=\"full-width\">\n<div><figcaption>\u041f\u0440\u0438\u043c\u0435\u0440 \u0434\u0430\u0448\u0431\u043e\u0440\u0434\u0430 \u0432 streamlit<\/figcaption><\/div>\n<\/figure>\n<details class=\"spoiler\">\n<summary>Python-\u0441\u043a\u0440\u0438\u043f\u0442<\/summary>\n<div class=\"spoiler__content\">\n<pre><code class=\"python\">from sqlalchemy import create_engine import pandas as pd import streamlit as st from streamlit_echarts import st_echarts import jinja2 import datetime import json from typing import Literal st.set_page_config(layout=\"wide\")  # \u0421\u043e\u0437\u0434\u0430\u0435\u043c \u0434\u0432\u0438\u0436\u043e\u043a \u043f\u043e\u0434\u043a\u043b\u044e\u0447\u0435\u043d\u0438\u044f engine = create_engine('postgresql+psycopg2:\/\/postgres:admin@localhost:5432\/database')  # \u043f\u043e\u043b\u0443\u0447\u0430\u0435\u043c \u0438 \u043a\u044d\u0448\u0438\u0440\u0443\u0435\u043c \u0434\u0430\u043d\u043d\u044b\u0435 spinner_text = \"\u0427\u0442\u0435\u043d\u0438\u0435 \u0434\u0430\u043d\u043d\u044b\u0445...\" @st.cache_data(ttl=300, max_entries=1000, show_spinner=spinner_text) def get_unique_values(column):     \"\"\"\u041f\u043e\u043b\u0443\u0447\u0430\u0435\u043c \u0443\u043d\u0438\u043a\u0430\u043b\u044c\u043d\u044b\u0435 \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u044f \u0441\u0442\u043e\u043b\u0431\u0446\u0430 \u0432\u0438\u0442\u0440\u0438\u043d\u044b\"\"\"     return  pd.read_sql_query(         f\"select distinct {column} from dm.mart_order order by {column}\",         engine)  @st.cache_data(ttl=300, max_entries=1000, show_spinner=spinner_text) def get_extreme_value(column, type: Literal[\"min\", \"max\"]):     \"\"\"\u041f\u043e\u043b\u0443\u0447\u0430\u0435\u043c \u044d\u043a\u0441\u0442\u0440\u0435\u043c\u0443\u043c\u044b\"\"\"     return  pd.read_sql_query(         f\"select {type}({column}) from dm.mart_order\",         engine).iloc[0, 0]  @st.cache_data(ttl=300, max_entries=1000, show_spinner=spinner_text) def get_order_data(         date_from = None,         date_to = None,         city = None,         product = None,         price = None ):     \"\"\"\u041f\u043e\u043b\u0443\u0447\u0430\u0435\u043c \u0434\u0430\u043d\u043d\u044b\u0435 \u0438\u0437 \u0432\u0438\u0442\u0440\u0438\u043d\u044b\"\"\"     with open('get_order_data.sql', \"r\", encoding=\"utf-8\") as f:         file = f.read()   <\/code><\/pre>\n<\/div>\n<\/details>\n<\/div>\n<\/div>\n<\/div>\n<\/div>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[],"tags":[],"class_list":["post-457779","post","type-post","status-publish","format-standard","hentry"],"_links":{"self":[{"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/posts\/457779","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=457779"}],"version-history":[{"count":0,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/posts\/457779\/revisions"}],"wp:attachment":[{"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=457779"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=457779"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=457779"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}