[{"data":1,"prerenderedAt":707},["ShallowReactive",2],{"news-item-\u002Fvi\u002Fnews\u002Ftruc-quan-hoa-du-lieu-bang-python-dash":3},{"id":4,"title":5,"body":6,"category":694,"created by":695,"date":696,"description":697,"extension":698,"meta":699,"navigation":700,"path":701,"sections":702,"seo":703,"stem":704,"thumbnail":705,"__hash__":706},"content_vi\u002Fvi\u002Fnews\u002Ftruc-quan-hoa-du-lieu-bang-python-dash.md","Trực quan hóa dữ liệu bằng Python Dash",{"type":7,"value":8,"toc":679},"minimark",[9,14,18,22,27,30,41,44,47,53,57,60,63,66,77,85,93,97,105,110,114,126,137,140,143,148,151,157,160,165,171,181,187,193,199,202,236,245,252,256,263,269,279,285,290,296,299,305,308,314,318,321,324,330,333,339,342,348,354,360,363,369,377,382,385,393,397,400,406,412,417,423,444,468,479,483,488,492,496,499,503,506,512,515,521,525,528,534,537,558,565,571,574,600,610,616,625,631,639,643,646,650,653,657,663,667,673],[10,11,13],"h1",{"id":12},"giới-thiệu-về-dash","Giới thiệu về Dash",[15,16,17],"p",{},"Dash là một thư viện mã nguồn mở được phát hành theo giấy phép MIT. Được viết trên Plotly.js và React.js, Dash lý tưởng cho việc xây dựng và triển khai các ứng dụng dữ liệu với UI (giao diện người dùng) tùy chỉnh. Dash đủ đơn giản để bạn có thể liên kết UI với code của mình trong vòng chưa đầy 10 phút. Ứng dụng Dash được render lên trình duyệt web, vì vậy nó có thể chạy trên đa nền tảng và cả thiết bị di động.",[10,19,21],{"id":20},"hướng-dẫn-sử-dụng-dash","Hướng dẫn sử dụng Dash",[23,24,26],"h2",{"id":25},"cài-đặt-dash","Cài đặt Dash",[15,28,29],{},"Yêu cầu máy cài sẵn Python 3. Trong terminal chạy lệnh sau để cài đặt Dash:",[31,32,37],"pre",{"className":33,"code":35,"language":36},[34],"language-text","pip install dash\n","text",[38,39,35],"code",{"__ignoreMap":40},"",[15,42,43],{},"Với lệnh trên thì ngoài dash, pip cũng sẽ cài đặt thư viện hỗ trợ vẽ đồ thị đó là Plotly.py.",[15,45,46],{},"Và cuối cùng chúng ta cần cài đặt thư viện Pandas bằng lệnh:",[31,48,51],{"className":49,"code":50,"language":36},[34],"pip install pandas\n",[38,52,50],{"__ignoreMap":40},[23,54,56],{"id":55},"về-pandas","Về Pandas",[15,58,59],{},"Pandas là một thư viện mã nguồn mở được phát hành theo giấy phép BSD. Pandas cung cấp các cấu trúc dữ liệu hiệu suất cao, dễ sử dụng và các công cụ phân tích dữ liệu cho ngôn ngữ lập trình Python.",[15,61,62],{},"Pandas cung cấp 2 cấu trúc dữ liệu chính là DataFrame và Series. DataFrame là một cấu trúc dữ liệu 2 chiều có thể lưu trữ dữ liệu thuộc các loại khác nhau (bao gồm ký tự, số nguyên, giá trị dấu phẩy động, dữ liệu phân loại và hơn thế nữa) trong các cột. Mỗi cột trong DataFrame là một Series.",[15,64,65],{},"Có ba quy ước phổ biến để lưu trữ dữ liệu dạng cột:",[67,68,69],"ul",{},[70,71,72,76],"li",{},[73,74,75],"strong",{},"Dữ liệu dạng dài"," có một hàng cho mỗi quan sát và một cột cho mỗi biến. Dạng này phù hợp để lưu trữ và hiển thị dữ liệu đa biến, tức là với số chiều lớn hơn 2.",[78,79],"img",{"className":80,"alt":40,"src":83,"style":84},[81,82],"block","mx-auto","https:\u002F\u002Fs3-ap-southeast-1.amazonaws.com\u002Fhomepage-media\u002Fwp-content\u002Fuploads\u002F2021\u002F12\u002F31142643\u002Flong_form_2.png","width: 50%;",[67,86,87],{},[70,88,89,92],{},[73,90,91],{},"Dữ liệu dạng rộng"," có một hàng cho mỗi giá trị của một trong các biến đầu tiên và một cột cho mỗi giá trị của biến thứ hai. Dạng này phù hợp để lưu trữ và hiển thị dữ liệu 2D.",[78,94],{"className":95,"alt":40,"src":96,"style":84},[81,82],"https:\u002F\u002Fs3-ap-southeast-1.amazonaws.com\u002Fhomepage-media\u002Fwp-content\u002Fuploads\u002F2021\u002F12\u002F31142738\u002Fwide_form_2.png",[67,98,99],{},[70,100,101,104],{},[73,102,103],{},"Dữ liệu dạng hỗn hợp"," là kết hợp của dữ liệu dạng dài và dữ liệu dạng rộng.",[78,106],{"className":107,"alt":40,"src":108,"style":109},[81,82],"https:\u002F\u002Fs3-ap-southeast-1.amazonaws.com\u002Fhomepage-media\u002Fwp-content\u002Fuploads\u002F2021\u002F12\u002F31142805\u002Fmixed_form.png","width: 70%;",[23,111,113],{"id":112},"dash-layout","Dash Layout",[15,115,116,117,121,122,125],{},"Ứng dụng Dash bao gồm hai phần. Phần đầu tiên là \"",[118,119,120],"em",{},"layout","\" của ứng dụng và nó mô tả ứng dụng đó trông như thế nào. Phần thứ hai mô tả khả năng tương tác của ứng dụng, nó là \"",[118,123,124],{},"callbacks\"",".",[15,127,128,129,132,133,136],{},"\"",[118,130,131],{},"Layout","\" là 1 cây tập hợp các \"",[118,134,135],{},"components","\".",[15,138,139],{},"Dash cung cấp rất nhiều loại component: Dash HTML Components, Dash Core Components, Dash DataTable, Dash DAQ, Dash Bootstrap Components,...",[15,141,142],{},"Trong khuôn khổ của bài viết chúng ta sẽ tìm hiểu Dash HTML Components và Dash Core Components.",[144,145,147],"h3",{"id":146},"dash-html-components","Dash HTML Components",[15,149,150],{},"Là fuction cung cấp các component kiểu HTML, dùng để định nghĩa các HTML tag cho layout.",[15,152,153,154],{},"Để dùng Dash HTML Components chúng ta cần import vào file .py như sau: ",[38,155,156],{},"from dash import html",[15,158,159],{},"Ví dụ:",[15,161,162],{},[118,163,164],{},"html_demo.py",[31,166,169],{"className":167,"code":168,"language":36},[34],"import dash\nfrom dash import html\n\napp = dash.Dash(__name__)\n\napp.layout = html.Div(children=[\n   html.H1(children='Hello Dash'),\n])\n\nif __name__ == '__main__':\n   app.run_server(debug=True)\n",[38,170,168],{"__ignoreMap":40},[15,172,173,176,177,180],{},[38,174,175],{},"html.H1(children='Hello Dash')"," sẽ tạo ra ",[38,178,179],{},"\u003Ch1>Hello Dash\u003C\u002Fh1>"," trên trình duyệt.",[15,182,183,184,136],{},"Cũng giống như các thẻ HTML tag, chúng ta hoàn toàn có thể thay đổi style của html_component bằng property \"",[118,185,186],{},"style",[15,188,189,190],{},"Ví dụ: ",[38,191,192],{},"html.H1('Hello Dash', style={'textAlign': 'center', 'color': '#7FDBFF'})",[15,194,195,196],{},"Đoạn mã trên được hiển thị dưới dạng ",[38,197,198],{},"\u003Ch1 style=\"text-align: center; color: #7FDBFF\">Hello Dash\u003C\u002Fh1>",[15,200,201],{},"Có một số khác biệt quan trọng giữa Dash HTML Components và các thuộc tính HTML:",[67,203,204,210,220,230],{},[70,205,206,207,209],{},"Thuộc tính \"",[118,208,186],{},"\" trong HTML là một chuỗi được phân tách bằng dấu chấm phẩy. Trong Dash, bạn cần cung cấp một dictionary.",[70,211,212,213,216,217,136],{},"Các key trong dictionary là dạng camelCased. Vì vậy, thay vì \"",[118,214,215],{},"text-align","\", trong Dash là \"",[118,218,219],{},"textAlign",[70,221,206,222,225,226,229],{},[118,223,224],{},"class","\" trong HTML là \"",[118,227,228],{},"className","\" trong Dash.",[70,231,232,233,136],{},"Con của thẻ HTML được chỉ định thông qua argument với từ khoá \"",[118,234,235],{},"children",[15,237,238,239],{},"Ngoài ra thay vì dùng style trực tiếp chúng ta có thể dùng file CSS để định nghĩa style cho layout, chi tiết tham khảo: ",[240,241,242],"a",{"href":242,"rel":243},"https:\u002F\u002Fdash.plotly.com\u002Fexternal-resources",[244],"nofollow",[15,246,247,248],{},"Bạn có thể xem tất cả các component có sẵn trong Dash HTML Components Gallery: ",[240,249,250],{"href":250,"rel":251},"https:\u002F\u002Fdash.plotly.com\u002Fdash-html-components",[244],[144,253,255],{"id":254},"dash-core-components","Dash Core Components",[15,257,258,259],{},"Bao gồm một tập hợp các thành phần cấp cao hơn như dropdown, checkbox, radio, graph, v.v. Bạn có thể xem tất cả các component có sẵn trong Dash Core Components Gallery: ",[240,260,261],{"href":261,"rel":262},"https:\u002F\u002Fdash.plotly.com\u002Fdash-core-components",[244],[15,264,265,266],{},"Để dùng Dash Core Components chúng ta cần import vào file .py như sau: ",[38,267,268],{},"from dash import dcc",[15,270,271,272,275,276,278],{},"Trong các core component thì \"",[118,273,274],{},"Graph","\" là component quan trọng đối với Trực quan hóa dữ liệu. \"",[118,277,274],{},"\" hiển thị trực quan hóa dữ liệu trên trình duyệt bằng cách sử dụng thư viện javascript vẽ đồ thị mã nguồn mở Plotly.js. Plotly.js hỗ trợ hơn 35 loại biểu đồ và hiển thị biểu đồ ở cả vector-quality SVG và high-performance WebGL. Một lưu ý nhỏ ở đây là Plotly.js chỉ dùng để render lên trình duyệt (do Dash thực hiện) còn khi code chúng ta sẽ dùng thư viện Plotly.py (được cung cấp sẵn khi cài đặt Dash) chứ không code trực tiếp bằng javascript.",[15,280,281,282,284],{},"Để biết cách sử dụng \"",[118,283,274],{},"\" component chúng ta hãy đến với ví dụ hiển thị data csv lên trình duyệt dưới dạng đồ thị đường gấp khúc:",[15,286,287],{},[118,288,289],{},"csv\u002Fgraph_sample.csv",[31,291,294],{"className":292,"code":293,"language":36},[34],"DateTime,DATA 1,DATA 2,DATA 3,DATA 4\n20211220 101010.000,30,100,124,197\n20211220 101010.010,40,110,134,65\n20211220 101010.020,50,140,214,149\n20211220 101010.030,60,150,169,-98\n20211220 101010.040,70,160,204,-173\n20211220 101010.050,80,170,164,-108\n20211220 101010.060,90,180,148,150\n20211220 101010.070,100,190,180,92\n20211220 101010.080,110,200,268,94\n20211220 101010.090,120,210,164,-139\n20211220 101010.100,130,220,254,-132\n",[38,295,293],{"__ignoreMap":40},[15,297,298],{},"Đầu tiên chúng ta cần dùng pandas để load file csv",[31,300,303],{"className":301,"code":302,"language":36},[34],"df = pd.read_csv('csv\u002Fgraph_sample.csv')\n",[38,304,302],{"__ignoreMap":40},[15,306,307],{},"In biến df ra console xem thử cấu trúc của nó",[31,309,312],{"className":310,"code":311,"language":36},[34],"print(df)\n",[38,313,311],{"__ignoreMap":40},[78,315],{"className":316,"alt":40,"src":317,"style":109},[81,82],"https:\u002F\u002Fs3-ap-southeast-1.amazonaws.com\u002Fhomepage-media\u002Fwp-content\u002Fuploads\u002F2021\u002F12\u002F31144341\u002Fexample_wide_form.png",[15,319,320],{},"Tới đây bạn có thấy hơi quen quen không? Chính xác, nó là Dữ liệu dạng rộng mà chúng ta đã đề cập ở phần tìm hiểu Pandas ở trên!",[15,322,323],{},"Bước tiếp theo chúng ta chuyển dữ liệu của cột DateTime từ string thành datetime để chart của chúng ta hiển thị chính xác ngày và giờ của dữ liệu",[31,325,328],{"className":326,"code":327,"language":36},[34],"df['DateTime'] = pd.to_datetime(df['DateTime'], format='%Y%m%d %H:%M:%S.%f')\n",[38,329,327],{"__ignoreMap":40},[15,331,332],{},"Bây giờ chúng ta tạo một line figure bằng plotly express",[31,334,337],{"className":335,"code":336,"language":36},[34],"line_fig = px.line(df, x='DateTime', y=['DATA 1', 'DATA 2', 'DATA 3', 'DATA 4'])\n",[38,338,336],{"__ignoreMap":40},[15,340,341],{},"Truyền figure vào Graph component",[31,343,346],{"className":344,"code":345,"language":36},[34],"app.layout = html.Div(children=[\n    dcc.Graph(id='graph', figure=line_fig)\n])\n",[38,347,345],{"__ignoreMap":40},[15,349,350,351],{},"Code hoàn chỉnh ",[118,352,353],{},"graph_demo.py",[31,355,358],{"className":356,"code":357,"language":36},[34],"import dash\nimport pandas as pd\nimport plotly.express as px\nfrom dash import dcc\nfrom dash import html\n\napp = dash.Dash(__name__)\n\ndf = pd.read_csv('csv\u002Fgraph_sample.csv')\nprint(df)\ndf['DateTime'] = pd.to_datetime(df['DateTime'], format='%Y%m%d %H:%M:%S.%f')\n\nline_fig = px.line(df, x='DateTime', y=['DATA 1', 'DATA 2', 'DATA 3', 'DATA 4'])\n\napp.layout = html.Div(children=[\n   dcc.Graph(id='graph', figure=line_fig)\n])\n\nif __name__ == '__main__':\n   app.run_server(debug=True)\n",[38,359,357],{"__ignoreMap":40},[15,361,362],{},"Trong terminal chạy lệnh:",[31,364,367],{"className":365,"code":366,"language":36},[34],"python graph_demo.py\n",[38,368,366],{"__ignoreMap":40},[15,370,371,372,376],{},"Sau đó truy cập ",[240,373,374],{"href":374,"rel":375},"http:\u002F\u002F127.0.0.1:8050\u002F",[244]," để xem kết quả",[78,378],{"className":379,"alt":40,"src":380,"style":381},[81,82],"https:\u002F\u002Fs3-ap-southeast-1.amazonaws.com\u002Fhomepage-media\u002Fwp-content\u002Fuploads\u002F2021\u002F12\u002F31144923\u002Fexample_graph-1024x373.png","width: 100%;",[15,383,384],{},"Trong ví dụ trên:",[67,386,387,390],{},[70,388,389],{},"Thư viện Pandas được dùng để xử lý data đầu vào (đọc csv, chuyển dữ liệu của cột DateTime từ string thành datetime).",[70,391,392],{},"Plotly Express (nằm trong thư viện Plotly.py) chịu trách nhiệm quy định kiểu biểu đồ (đường gấp khúc, phân tán,...), x-axis, y-axis,... của Graph đầu ra.",[144,394,396],{"id":395},"dash-callbacks","Dash Callbacks",[15,398,399],{},"Callback functions: các hàm được Dash tự động gọi bất cứ khi nào thuộc tính của input component thay đổi, để cập nhật một số thuộc tính trong component khác (output).",[15,401,402,403],{},"Để hiểu về Callbacks chúng ta hãy đến với ví dụ về filter dữ liệu theo ngày, với input lấy từ component dcc.DatePickerRange: ",[118,404,405],{},"csv\u002Fcallbacks_sample.csv",[31,407,410],{"className":408,"code":409,"language":36},[34],"DateTime,DATA 1,DATA 2,DATA 3,DATA 4\n20211219 101010.010,10,200,178,90\n20211219 111010.020,20,150,134,25\n20211219 121010.030,5,130,210,11\n20211219 131010.040,15,110,100,-97\n20211219 141010.050,60,150,143,-17\n20211219 151010.060,30,140,132,30\n20211219 161010.070,20,180,167,45\n20211219 171010.080,16,120,240,123\n20211219 181010.090,75,190,153,40\n20211219 191010.100,90,250,162,-10\n20211220 001010.000,68,142,156,1\n20211220 011010.010,40,110,134,65\n20211220 021010.020,50,140,214,149\n20211220 031010.030,60,150,169,-98\n20211220 041010.040,70,160,204,-173\n20211220 051010.050,80,170,164,-108\n20211220 061010.060,90,180,148,150\n20211220 071010.070,100,190,180,92\n20211220 081010.080,110,200,268,94\n20211220 091010.090,120,210,164,-139\n20211220 101010.100,130,220,254,-132\n20211221 001010.000,10,90,142,30\n20211221 011010.010,30,100,162,55\n20211221 021010.020,80,120,180,20\n20211221 031010.030,70,110,176,-10\n20211221 041010.040,50,130,194,-90\n20211221 051010.050,60,140,202,-120\n20211221 061010.060,90,150,164,100\n20211221 071010.070,120,160,197,132\n20211221 081010.080,110,170,186,40\n20211221 091010.090,130,210,182,-130\n20211221 101010.100,120,230,210,-100\n",[38,411,409],{"__ignoreMap":40},[15,413,414],{},[118,415,416],{},"callbacks_demo.py",[31,418,421],{"className":419,"code":420,"language":36},[34],"from datetime import datetime, timedelta\n\nimport dash\nimport pandas as pd\nimport plotly.express as px\nfrom dash import dcc, Output, Input\nfrom dash import html\n\napp = dash.Dash(__name__)\n\ndf = pd.read_csv('csv\u002Fcallbacks_sample.csv')\ndf['DateTime'] = pd.to_datetime(df['DateTime'], format='%Y%m%d %H:%M:%S.%f')\n\ninit_start_date = df['DateTime'].min().strftime('%Y-%m-%d')\ninit_end_date = df['DateTime'].max().strftime('%Y-%m-%d')\n\napp.layout = html.Div(children=[\n   dcc.DatePickerRange(\n       id='date-picker-range',\n       start_date=init_start_date,\n       end_date=init_end_date,\n       minimum_nights=0,\n       display_format='YYYY\u002FMM\u002FDD'\n   ),\n   dcc.Graph(id='scatter-graph'),\n])\n\n@app.callback(\n   Output('scatter-graph', 'figure'),\n   Input('date-picker-range', 'start_date'),\n   Input('date-picker-range', 'end_date')\n)\ndef update_figure(start_date, end_date):\n   if start_date is not None and end_date is not None:\n       start_date = datetime.fromisoformat(start_date)\n       end_date = datetime.fromisoformat(end_date) + timedelta(days=1)\n       filtered_df = df[(start_date \u003C= df['DateTime']) & (df['DateTime'] \u003C= end_date)]\n       scatter_fig = px.scatter(filtered_df, x='DateTime', y=['DATA 1', 'DATA 2', 'DATA 3', 'DATA 4'])\n\n       return scatter_fig\n\nif __name__ == '__main__':\n   app.run_server(debug=True)\n",[38,422,420],{"__ignoreMap":40},[15,424,425,426,429,430,433,434,437,438,433,441,136],{},"Trong Dash, các input và output của ứng dụng của chúng ta chỉ đơn giản là các property của một component cụ thể. Trong ví dụ này, input của chúng ta là property \"",[118,427,428],{},"start_date","\" và \"",[118,431,432],{},"end_date","\" của component có ID \"",[118,435,436],{},"date-picker-range","\". Output của chúng ta là property \"",[118,439,440],{},"figure",[118,442,443],{},"scatter-graph",[15,445,446,447,450,451,454,455,457,458,460,461,463,464,467],{},"Bất cứ khi nào input property thay đổi, function mà có khai báo decorator ",[118,448,449],{},"@callback"," sẽ được gọi tự động. Dash cung cấp cho callback function này giá trị mới của input property làm argument (trong ví dụ trên function ",[118,452,453],{},"update_figure"," có 2 argument là ",[118,456,428],{},", ",[118,459,432],{},"), và Dash cập nhật property của output component với bất kỳ giá trị nào được function trả về (trong ví dụ trên function ",[118,462,453],{}," trả về ",[118,465,466],{},"scatter_fig",").",[15,469,470,471,474,475,478],{},"Trong terminal chạy lệnh ",[38,472,473],{},"python callbacks_demo.py"," và truy cập vào ",[240,476,374],{"href":374,"rel":477},[244]," để xem kết quả.",[78,480],{"className":481,"alt":40,"src":482,"style":381},[81,82],"https:\u002F\u002Fs3-ap-southeast-1.amazonaws.com\u002Fhomepage-media\u002Fwp-content\u002Fuploads\u002F2021\u002F12\u002F31145606\u002Fcallback_demo_pic1-1024x381.png",[15,484,485,486],{},"Sau khi thay đổi ",[118,487,432],{},[78,489],{"className":490,"alt":40,"src":491,"style":381},[81,82],"https:\u002F\u002Fs3-ap-southeast-1.amazonaws.com\u002Fhomepage-media\u002Fwp-content\u002Fuploads\u002F2021\u002F12\u002F31145648\u002Fcallbacks_demo_pic2-1024x387.png",[23,493,495],{"id":494},"tối-ưu-hóa-và-thêm-chức-năng","Tối ưu hóa và thêm chức năng",[15,497,498],{},"Ở phần này chúng ta lấy code ở phần Callbacks để tối ưu hóa và thêm chức năng cho nó.",[144,500,502],{"id":501},"đọc-n-data","Đọc n DATA",[15,504,505],{},"Hiện tại chúng ta đang set cứng số lượng data đầu vào là 4.",[31,507,510],{"className":508,"code":509,"language":36},[34],"scatter_fig = px.scatter(filtered_df, x='DateTime', y=['DATA 1', 'DATA 2', 'DATA 3', 'DATA 4'])\n",[38,511,509],{"__ignoreMap":40},[15,513,514],{},"Giả sử chúng ta có số lượng data bất kỳ DATA 1, DATA 2,..., DATA n thì với đoạn code trên chúng ta chỉ có thể đọc và hiển thị được 4 data mà thôi. Để đọc và hiển thị lên biểu đồ n DATA, chúng ta cần chỉnh sửa code lại một chút:",[31,516,519],{"className":517,"code":518,"language":36},[34],"# get first columns name for x-axis\nx_col_name = df.columns[0]\n# get list column name except first column for y-axis\ny_col_name_list = df.columns[1:]\nfiltered_df = df[(start_date \u003C= df[x_col_name]) & (df[x_col_name] \u003C= end_date)]\nscatter_fig = px.scatter(filtered_df, x=x_col_name, y=y_col_name_list)\n",[38,520,518],{"__ignoreMap":40},[144,522,524],{"id":523},"đọc-config-từ-header-của-csv","Đọc config từ header của CSV",[15,526,527],{},"Xét header của CSV như sau",[31,529,532],{"className":530,"code":531,"language":36},[34],"DateTime(yyyyMMdd HH:mm:ss.fff),DATA 1(minFilter=20;maxFilter=100),DATA 2(maxFilter=140),DATA 3,DATA 4,DATA 5\n",[38,533,531],{"__ignoreMap":40},[15,535,536],{},"Chúng ta sẽ thêm chức năng đọc config từ header trên:",[67,538,539,542],{},[70,540,541],{},"Đọc config của cột DateTime để set format date time (hiện tại đang set cứng trong code).",[70,543,544,545,457,548,551,552,554,555,557],{},"Đọc config ",[118,546,547],{},"minFilter",[118,549,550],{},"maxFilter"," của các cột DATA để lọc bỏ những data có giá trị nhỏ hơn ",[118,553,547],{}," và lớn hơn ",[118,556,550],{}," của cột DATA đó.",[15,559,560,561,564],{},"Đầu tiên thêm file ",[118,562,563],{},"utils.py"," chứa các function common",[31,566,569],{"className":567,"code":568,"language":36},[34],"import re\n\n_format_convertor = (\n   ('yyyy', '%Y'), ('yyy', '%Y'), ('yy', '%y'), ('y', '%y'),\n   ('MMMM', '%B'), ('MMM', '%b'), ('MM', '%m'), ('M', '%m'),\n   ('dddd', '%A'), ('ddd', '%a'), ('dd', '%d'), ('d', '%d'),\n   ('HH', '%H'), ('H', '%H'), ('hh', '%I'), ('h', '%I'),\n   ('mm', '%M'), ('m', '%M'),\n   ('ss', '%S'), ('s', '%S'),\n   ('tt', '%p'), ('t', '%p'),\n   ('fff', '%f'),\n   ('zzz', '%z'), ('zz', '%z'), ('z', '%z'),\n)\n\ndef convert_py_datetime_format(in_format):\n   out_format = ''\n   while in_format:\n       if in_format[0] == \"'\":\n           apos = in_format.find(\"'\", 1)\n           if apos == -1:\n               apos = len(in_format)\n           out_format += in_format[1:apos].replace('%', '%%')\n           in_format = in_format[apos + 1:]\n       elif in_format[0] == '\\\\':\n           out_format += in_format[1:2].replace('%', '%%')\n           in_format = in_format[2:]\n       else:\n           for intok, outtok in _format_convertor:\n               if in_format.startswith(intok):\n                   out_format += outtok\n                   in_format = in_format[len(intok):]\n                   break\n           else:\n               out_format += in_format[0].replace('%', '%%')\n               in_format = in_format[1:]\n   return out_format\n\ndef extract_csv_col_config(col_name: str):\n   try:\n       found = re.search('\\\\((.*)\\\\)', col_name)\n       col_name = col_name.replace(found.group(0), '')\n       config_string = found.group(1)\n       config_list = config_string.split(';')\n       configs = []\n       for config in config_list:\n           key_value_list = config.split('=')\n           key = key_value_list[0]\n           value = key_value_list[1] if len(key_value_list) > 1 else None\n           configs.append((key, value))\n   except AttributeError:\n       configs = []\n   return col_name, configs\n",[38,570,568],{"__ignoreMap":40},[15,572,573],{},"Ở đoạn code trên:",[67,575,576,583],{},[70,577,578,579,582],{},"Function ",[118,580,581],{},"convert_py_datetime_format"," dùng để chuyển đổi format kiểu yyyyMMdd HH:mm:ss.fff sang dạng format của Python.",[70,584,578,585,588,589,592,593,596,597],{},[118,586,587],{},"extract_csv_col_config"," sẽ nhận vào tên của cột chứa config và trả về tên cột đã cắt bỏ phần string config cùng với array chứa các config của cột đó. Ví dụ ",[38,590,591],{},"DATA 1(minFilter=20;maxFilter=100)"," sẽ trả về ",[38,594,595],{},"DATA 1"," và array ",[38,598,599],{},"[(minFilter, 20), (maxFilter, 100)]",[15,601,602,603,606,607],{},"Tiếp theo thêm function ",[118,604,605],{},"process_csv_variable"," vào ",[118,608,609],{},"app.py",[31,611,614],{"className":612,"code":613,"language":36},[34],"from datetime import datetime, timedelta\n\nimport dash\nimport numpy as np\nimport pandas as pd\nimport plotly.express as px\nfrom dash import dcc, Output, Input\nfrom dash import html\n\nfrom utils import extract_csv_col_config, convert_py_datetime_format\n\ndef process_csv_variable(df_param):\n   # process x-axis csv variable\n   old_x_col_name = df_param.columns[0]\n   new_x_col_name, configs = extract_csv_col_config(old_x_col_name)\n   datetime_format = configs[0][0]\n   df_param = df_param.rename(columns={old_x_col_name: new_x_col_name})\n   df_param[new_x_col_name] = pd.to_datetime(df_param[new_x_col_name],\n                                             format=convert_py_datetime_format(datetime_format))\n   # process y-axis csv variable\n   y_col_name_list = df_param.columns[1:]\n   for old_y_col_name in y_col_name_list:\n       new_y_col_name, configs = extract_csv_col_config(old_y_col_name)\n       df_param = df_param.rename(columns={old_y_col_name: new_y_col_name})\n       for config, value in configs:\n           if config == 'minFilter':\n               df_param.loc[df_param[new_y_col_name] \u003C int(value), new_y_col_name] = np.nan\n           elif config == 'maxFilter':\n               df_param.loc[df_param[new_y_col_name] > int(value), new_y_col_name] = np.nan\n   return df_param\n\napp = dash.Dash(__name__)\n\napp.layout = html.Div(id='container', children=[\n   dcc.DatePickerRange(\n       id='date-picker-range',\n       minimum_nights=0,\n       display_format='YYYY\u002FMM\u002FDD'\n   ),\n   dcc.Graph(id='scatter-graph'),\n])\n\n@app.callback(\n   Output('date-picker-range', 'start_date'),\n   Output('date-picker-range', 'end_date'),\n   Input('container', 'id')\n)\ndef update_date_picker(id):\n   df = pd.read_csv('csv\u002Fapp_sample.csv')\n   df = process_csv_variable(df)\n   x_col_name = df.columns[0]\n\n   init_start_date = df[x_col_name].min().strftime('%Y-%m-%d')\n   init_end_date = df[x_col_name].max().strftime('%Y-%m-%d')\n   return init_start_date, init_end_date\n\n@app.callback(\n   Output('scatter-graph', 'figure'),\n   Input('date-picker-range', 'start_date'),\n   Input('date-picker-range', 'end_date')\n)\ndef update_figure(start_date, end_date):\n   df = pd.read_csv('csv\u002Fapp_sample.csv')\n   df = process_csv_variable(df)\n   if start_date is not None and end_date is not None:\n       start_date = datetime.fromisoformat(start_date)\n       end_date = datetime.fromisoformat(end_date) + timedelta(days=1)\n       # get first columns name for x-axis\n       x_col_name = df.columns[0]\n       # get list column name except first column for y-axis\n       y_col_name_list = df.columns[1:]\n       filtered_df = df[(start_date \u003C= df[x_col_name]) & (df[x_col_name] \u003C= end_date)]\n       scatter_fig = px.scatter(filtered_df, x=x_col_name, y=y_col_name_list)\n\n       return scatter_fig\n\nif __name__ == '__main__':\n   app.run_server(debug=True)\n",[38,615,613],{"__ignoreMap":40},[15,617,578,618,620,621,624],{},[118,619,605],{}," sẽ nhận vào DataFrame, đọc config từ tên cột, xử lý data dựa theo config và sẽ trả về DataFrame sau khi xử lý. Bây giờ chúng ta thêm file ",[118,622,623],{},"csv\u002Fapp_sample.csv"," để test",[31,626,629],{"className":627,"code":628,"language":36},[34],"DateTime(yyyyMMdd HH:mm:ss.fff),DATA 1(minFilter=20;maxFilter=100),DATA 2(maxFilter=140),DATA 3,DATA 4,DATA 5\n20211219 101010.010,10,200,178,90,110\n20211219 111010.020,20,150,134,25,120\n20211219 121010.030,5,130,210,11,90\n20211219 131010.040,15,110,100,-97,80\n20211219 141010.050,60,150,143,-17,130\n20211219 151010.060,30,140,132,30,140\n20211219 161010.070,20,180,167,45,150\n20211219 171010.080,16,120,240,123,160\n20211219 181010.090,75,190,153,40,150\n20211219 191010.100,90,250,162,-10,170\n20211220 001010.000,68,142,156,1,180\n20211220 011010.010,40,110,134,65,130\n20211220 021010.020,50,140,214,149,190\n20211220 031010.030,60,150,169,-98,200\n20211220 041010.040,70,160,204,-173,190\n20211220 051010.050,80,170,164,-108,180\n20211220 061010.060,90,180,148,150,170\n20211220 071010.070,100,190,180,92,150\n20211220 081010.080,110,200,268,94,160\n20211220 091010.090,120,210,164,-139,140\n20211220 101010.100,130,220,254,-132,130\n20211221 001010.000,10,90,142,30,150\n20211221 011010.010,30,100,162,55,160\n20211221 021010.020,80,120,180,20,170\n20211221 031010.030,70,110,176,-10,110\n20211221 041010.040,50,130,194,-90,90\n20211221 051010.050,60,140,202,-120,80\n20211221 061010.060,90,150,164,100,70\n20211221 071010.070,120,160,197,132,60\n20211221 081010.080,110,170,186,40,50\n20211221 091010.090,130,210,182,-130,40\n20211221 101010.100,120,230,210,-100,30\n",[38,630,628],{"__ignoreMap":40},[15,632,470,633,474,636,478],{},[38,634,635],{},"python app.py",[240,637,374],{"href":374,"rel":638},[244],[78,640],{"className":641,"alt":40,"src":642,"style":381},[81,82],"https:\u002F\u002Fs3-ap-southeast-1.amazonaws.com\u002Fhomepage-media\u002Fwp-content\u002Fuploads\u002F2021\u002F12\u002F31150435\u002Fapp_demo1-1024x371.png",[15,644,645],{},"Để dễ kiểm tra kết quả chúng ta ẩn các data khác chỉ hiển thị DATA 1",[78,647],{"className":648,"alt":40,"src":649,"style":381},[81,82],"https:\u002F\u002Fs3-ap-southeast-1.amazonaws.com\u002Fhomepage-media\u002Fwp-content\u002Fuploads\u002F2021\u002F12\u002F31150508\u002Fapp_demo2-1024x373.png",[15,651,652],{},"Chúng ta thấy các data có value nhỏ hơn 20 và lớn hơn 100 đã bị lọc bỏ.",[10,654,656],{"id":655},"source-code","Source code",[15,658,659],{},[240,660,661],{"href":661,"rel":662},"https:\u002F\u002Fgitlab.com\u002Fbwv-hp\u002Fpython-dash-sample",[244],[10,664,666],{"id":665},"tài-liệu-tham-khảo","Tài liệu tham khảo",[15,668,669],{},[240,670,671],{"href":671,"rel":672},"https:\u002F\u002Fdash.plotly.com\u002F",[244],[15,674,675],{},[240,676,677],{"href":677,"rel":678},"https:\u002F\u002Fpandas.pydata.org\u002Fdocs\u002F",[244],{"title":40,"searchDepth":680,"depth":680,"links":681},2,[682,683,684,690],{"id":25,"depth":680,"text":26},{"id":55,"depth":680,"text":56},{"id":112,"depth":680,"text":113,"children":685},[686,688,689],{"id":146,"depth":687,"text":147},3,{"id":254,"depth":687,"text":255},{"id":395,"depth":687,"text":396},{"id":494,"depth":680,"text":495,"children":691},[692,693],{"id":501,"depth":687,"text":502},{"id":523,"depth":687,"text":524},"tech talk","NGHIA NGHUYEN TRUNG","2022-01-25","Giới thiệu về Dash Dash là một thư viện mã nguồn mở được phát hành theo giấy phép MIT. Được viết trên Plotly.js và React.js, Dash lý tưởng cho việc xây dựng và triển khai các ứng dụng dữ liệu với UI (giao diện người dùng) tùy chỉnh. Dash đủ đơn giản để bạn có thể liên kết UI với code của mình trong vòng chưa đầy 10 phút. Ứng dụng Dash được render lên trình duyệt web, vì vậy nó có thể chạy trên đa nền tảng và cả thiết bị di động.","md",{},true,"\u002Fvi\u002Fnews\u002Ftruc-quan-hoa-du-lieu-bang-python-dash",null,{"title":5,"description":697},"vi\u002Fnews\u002Ftruc-quan-hoa-du-lieu-bang-python-dash","https:\u002F\u002Fhomepage-media.s3.ap-southeast-1.amazonaws.com\u002Fwp-content\u002Fuploads\u002F2026\u002F06\u002F04082100\u002Fpython_dash.png","tjesywSY2PQbUnh-0WLicZZzj-m7fw6Dxlys189Xhhc",1782205038729]