My Blog List

Thursday, September 4, 2014

[D3js] ทำความรู้จักกับ D3js และอะไรที่ทำให้มัน น่าสนใจ (ต่อ) (2/2)

ต่อจากบล๊อคที่แล้วนะครับ
วันนี้ผมมีหัวข้อน่าสนใจดังนี้

1. การใช้ Range, Domain เป็น Map
2. เทคนิค

1. เริ่มที่ การใช้ Range, Domain
เป็นการทำ Scale อย่างนึงครับ

(รูป)
ที่มา: https://www.dashingd3js.com/d3js-scales

หลักการคือ เราจะแปลงข้อมูลจริงๆ (Domain) ไปยังข้อมูลที่เราต้องการ (Range)

และ D3js ก็มีให้เราใช้หลายชนิด มี Linear คือแปลง Domain มาเป็น Range ด้วยการเทียบบรรยัติไตรยางค์ธรรมดาเลย, มีแบบ Ordinal คือ Scale แบบตรงๆ เช่น Domain เป็น [1,2,3,4,5] และ Range เป็น [7,8,9,10,11] เวลาเราใส่ 4 ผลลัพธ์ก็จะได้ 10 เป็นต้นครับ และเจ้า Ordinal Scale นี่เอง ที่เราประยุกต์ใช้เป็น Map ได้ สบายๆ

http://www.d3noob.org/2012/12/setting-scales-domains-and-ranges-in.html

เว็บนี้ก็อธิบายเรื่อง Scale ไว้อย่างละเอียดครับ ลองไปศึกษากันได้
D3js มี Document ค่อนข้างเยอะครับ เพราะฉะนั้น ไม่ต้องเป็นห่วงเรื่องนี้เลย

2. เทคนิค
ออกตัวก่อนว่า ผมไม่ได้มีความรู้เรื่องการ Design ที่ถูกต้องเท่าไหร่
แต่ได้ทำเกี่ยวกับ D3js มา และพอจะจับทางได้ว่า ควร Design ไปทางไหน
หากใครที่มีหลักการที่ดีกว่านี้ ก็ไม่ว่ากันครับ

- จัดกลุ่มของกราฟ ด้วยอะไรก็ได้ แต่ผมชอบจัดกลุ่มด้วย Tag G แล้วก็ตั้งชื่อ เช่นผมกำลังจะวาด Legend คำอธิบายกราฟ) ผมก็สร้าง <g class="legend">....</g> ครอบเอาไว้ เพื่อความเป็นหมวดหมู่
และจัด CSS ง่ายด้วย

- การจัดตำแหน่ง ในบางครั้งเราจำเป็นต้องจัดตำแหน่งให้วัตถุ A อยู่ใต้ข้อความ B แต่เนื่องจาก เราไม่สามารถรู้ความสูงของข้อความ B ได้จาก Font Size ธรรมดา
วิธีการของผมก็คือ ให้วาด Font B ก่อน แล้วหลังจากนั้น การจัดวัตถุ A ก็จะง่ายขึ้น
เพราะว่า เราสามารถหาความสูง ความกว้าง ของสิ่งใดๆ ใน SVG ได้หมดครับ

- ตรวจสอบข้อมูล ควรจะตรวจสอบด้วยครับ เช่นผมเคยเจอเหตุการณ์นี้ ต้องการวาดกราฟเส้น โดยคาดหวังว่า ข้อมูลจะส่งมา 5 จุด แต่จริงๆ ข้อมูลส่งมา 3 จุด (คือจุดแรก จุดที่สอง และจุดสุดท้าย)
กราฟของผมวาดได้ครับ ทั้งๆที่ ข้อมูลของจุดที่ สาม สี่ หายไป
เพราะว่าการวาดกราฟเส้น เราต้องเอาจุดต่อจุดอยู่แล้ว
ทำให้การอ่านกราฟ ผิดไปเลย กรณีนี้ต้องระวังครับ

- การจัดความกว้าง ความยาวของพื้นที่กราฟ ผมแนะนำจัดแบบสัดส่วน
คือสมมติว่าเราออกแบบว่า
มี Title อยู่ด้านบน จัดกลาง
มี Line Graph อยู่ถัดลงมา ชิดซ้าย
มี Legend อยู่ ชิดขวา แต่อยู่ตำแหน่งในแนวแกน Y เดียวกับ Line Graph

Title เราควรกำหนดให้ Width = 100%
Line Graph กำหนด Width = 70%
Legend กำหนด Width = 30%

แบบนี้เป็นต้นครับ
เราไม่ควรกำหนดเป็น Pixel มิเช่นนั้นการขยายกราฟ หรือย่อกราฟ ในภายหลัง จะปวดหัวมาก
เพราะเราต้องไปไล่แก้ความกว้างของทุกอย่างในกราฟ

จบแล้วครับ
หวังว่า คงพอจะเห็นภาพเบื้องต้น เกี่ยวกับ D3js



Tuesday, August 12, 2014

[D3js] ทำความรู้จักกับ D3js และอะไรที่ทำให้มัน น่าสนใจ (1/2)

สวัสดีครับ
วันนี้ผมมานำเสนอเรื่องราวเกี่ยวกับ D3js (Data-Driven Documents) เป็นการใช้ javascript โดยเอา Data ไปผูกไว้ (เดี๋ยวผมจะอธิบายอีกทีนะครับ ว่ามันคือยังไง)
สามารถเข้าไปติดตามได้ที่ Official d3js website ที่ d3js.org ครับ

บล๊อกนี้จะมีสองส่วน คือ ส่วนของ Coding และ Design นะครับ

Coding
- การติดตั้ง
โหลดไฟล์ d3.min.js มาจาก Official Website แล้วแค่ import มาใช้ก็เรียบร้อยแล้ว
ต่อไปมาดูความสามารถของเจ้า d3js กันนะครับ ผมจะยกบาง Features ที่เด่นๆ แปลกๆ มาเล่าให้ฟัง

หนึ่งในความสามารถของ d3 คือการ Select ซึ่งก็คล้ายๆกับ Selector ของเจ้าอื่น ที่สามารถเลือกได้ทั้ง id, tag, class แต่ความสามารถของมันจริงๆที่ซ่อนอยู่ ก็คือ การ Select Node ที่ไม่มีอยู่จริงได้ด้วย
มันจะสร้างเป็น Virtual node เอาไว้ เวลาที่มันหาไม่เจอ แต่เจ้า Virtual node นี้ไม่ใช่ Concrete นะครับ คือไม่มีตัวตนอยู่จริงๆ (แต่เราก็สามารถทำให้มันมีตัวตนจริงๆได้)
มาชมตัวอย่างกันซักหน่อย เดี๋ยวจะนึกภาพตามไม่ออก
ตัวอย่างข้างบนนี้แสดงให้เห็นว่า เราสามารถ Select P tag ได้สบายๆ 
ลองมาดูอีกครั้งครับ
จากตัวอย่างนี้ แสดงให้เห็นว่า เราก็สามารถ Select H tag ซึ่งตอนนี้มันไม่มีอยู่จริงได้เหมือนกัน
เจ้าตัวนี้แหละครับ มันคือ Virtual Node ที่ทำให้ D3 เจ๋ง

และมีการใช้ selectAll เอาไว้ Select หลายๆตัว แล้วจัดการอะไรพร้อมๆกัน
เช่น

ต่อไปคือใจความสำคัญของเจ้า d3js เลยครับ นั่นคือ data()
คือเราสามารถแปะข้อมูล เข้าไปกับ Node ใดๆ ก็ได้ และเรียกใช้เมื่อใดก็ได้ (ข้อมูลจะถูกแปะอยู่ที่ Node อย่างนั้น ไม่หายไป)
มาดูตัวอย่างกันนะครับ
สังเกตว่า ผม "แปะ" data ที่เป็น Array มีสมาชิกตัวเดียว คือ 1 เข้าไปให้กับ G tag
เลข 1 ของผม ก็จะเข้าไปอยู่ใน G tag ในตัวแปรที่ชื่อ data ครับ

*เพิ่มเติมนะครับ .data() พารามิเตอร์ที่จะส่งไป ต้องเป็น Array นะครับ ถ้าคุณมีตัวแปรที่เป็น object ก็แค่ใส่ [ ] คร่อมมันไป ก็จบแล้วครับ

ต่อไป มาใช้ความสามารถของ d3 กันให้เต็มที่หน่อย
โดยการ select tag ที่ไม่มีอยู่จริง แล้วเอา data เข้าไปแปะ แล้วดูว่าผลลัพธ์มันจะเป็นยังไง
ในที่นี้ ผม SelectAll G tag ซึ่งมันไม่มีอยู่จริง ก็จะเลยสร้าง Virtual Node ให้ผม
แล้วเมื่อผมเอา ข้อมูลไป "แปะ" แต่ข้อมูลดันเป็น Array 3 ตัว
ผลลัพธ์ก็คือ ผมจะได้ Virtual Node มา 3 ตัว
โดยที่
Virtual node ตัวแรก มี data = 1
Virtual node ตัวที่สอง มี data = 2
Virtual node ตัวที่สาม มี data = 3
ตามลำดับครับ

แต่การมี Virtual node ก็ไม่สามารถเอาไปทำอะไรต่อได้ นอกเสียจากเราเปลี่ยนมัน ให้กลายเป็น Node จริงๆซะก่อน ด้วยคำสั่ง .enter() มันจะสร้าง Virtual node ขึ้นเป็น Concrete Node  มีเนื้อ มีหนัง จับได้ ลูบได้ อูวววว์ (จะเสียวทำไม?)

เมื่อเราสั่ง .enter() แล้ว เราต้องสั่ง .append("g") เข้าไปด้วย เพราะมันจะไม่รู้ว่าเรากำลังจะสร้าง Node อะไร
(คือ ไอเจ้า Virtual Node เนี่ย มันเป็นแค่ตัวแปรเบาๆ ไม่ได้มีบอกว่าเป็น Virtual  Node ของ Tag ชนิดไหน)

มาดูตัวอย่างกัน!!!

จะเห็นว่า เมื่อเติม .enter().append("g") เข้าไปแล้ว
จะเห็น ว่ามันสร้าง G tag ขึ้นมาให้เราจริงๆ ... ว้าววว เยี่ยมไปเลย
แถม G แต่ละตัว ยังแปะ data เป็น 1,2,3 ตามลำดับ ให้เราเรียกใช้ได้ภายหลังอีกด้วย

ทีนี้มาดูตัวอย่างการใช้ข้อมูลที่อยู่ในตัวแปว data ที่เรา แปะ เข้าไปกันบ้างครับ
จากตัวอย่างข้างบนนี้ เราจะรู้ว่า Default Parameter ที่ถูกส่งให้ function ต่างๆ มันก็คือตัวแปร data นั่นเอง
เราเอาไปใช้ได้ตามใจของเราเลย
สะดวกดีมั้ยล่ะครับ เขียนบรรทัดเดียว ก็สามารถสร้าง Element ได้พร้อมๆกัน หลายตัว

*Note นิดนึงครับ
คือหากเรา แปะ data ให้กับ Node ใดๆแล้ว
data จะถูกส่งผ่าน ไปยังลูกหลานของมันด้วยครับ เวลาเราสร้าง Element ลูก, มันก็จะมี data นั้นด้วย
เราก็สามารถเปลี่ยน data ได้นะครับ แค่มัน Default ส่งไปให้ด้วย เฉยๆ สะดวกดี

หรือเขียนแบบนี้ก็ได้
ผมสร้างฟังก์ชันขึ้นมา แล้วส่งฟังก์ชันนั้น เข้าไปใน .text() ตรงๆเลย
สังเกตว่า ผมไม่ได้ส่งพารามิเตอร์ ใดๆ ให้ฟังก์ชันเลย มันจะรู้เอง ว่าเรากำลังหมายถึง data แน่ๆ


เอาละครับ บล๊อกนี้จะยาวเกินไปแล้ว
ผมตัดจบตอนที่หนึ่งดีกว่า
บล๊อกถัดไป ผมจะพูดถึงเรื่อง Fantasy drawing function อื่นๆ


- fantasy drawing function (path, arc)
- domain, range good to know

Desing
- การใช้ graph group
- การออกแบบแบบ scalable

[SQL Server] Query ข้อมูลแยกตามเวลา โดยกำหนด Hour_Interval เอง

สวัสดีครับ
การ Query บางทีก็ต้องการข้อมูลเป็น ช่วงของเวลา
เช่น

2014-6-16 02:00:00  A
2014-6-16 04:00:00  B
2014-6-16 06:00:00  C
2014-6-16 08:00:00  D

คงต้องมีให้ใช้กันบ้างใช่ไหมครับ
นั่งคิดอยู่นานว่าจะทำยังไง
จนคิดวิธีนี้ออก วิธียัดสมการคำนวนวันที่ไปตรงๆนี่แหละครับ ฮ่าๆๆ
ไม่ค่อยได้ใช้ความสามารถของ sqlserver ซักเท่าไหร่

ลองดูกันนะครับ ...

ก่อนอื่นก็เตรียมตัวแปรที่จำเป็นให้เรียบร้อย

DECLARE @start_date DATETIME = '2014-07-06 21:00:00' ;
DECLARE @end_date DATETIME = '2014-07-08 5:00:00' ;
DECLARE @i INT = 7;

@i ก็คือตัวแปร ที่ใช้บอกว่า "เอาข้อมูลทุกๆ กี่ชั่วโมง" นะครับ

พอตระเตรียมตัวแปรกันเสร็จก็บรรเลงเลย
;WITH dta AS
(
        SELECT
               MIN(booking_date ) AS 'Date',
               COUNT(booking_id ) AS 'Total_Bookings'
        FROM    myTable (nolock)
        WHERE   booking_date between @start_date and @end_date
        GROUP BY (DATEDIFF( DAY,@start_date ,booking_date)* 24+DATEPART (HOUR, booking_date)-DATEPART (HOUR, @start_date))/@i
)

SELECT * FROM dta ORDER BY dta.[Date] ;

อธิบายโค้ดหน่อย
การใช้ ;WITH (name) AS (...)
เป็นการสร้าง TEMP VARIABLE แบบใช้ได้แค่ อีกครั้งเดียว พอใช้เสร็จ ตัวแปรนี้ก็จะหายไป
เช่น
ผมสร้าง
;WITH dta AS
(
   SELECT * FROM myTable;
เนี่ย พอสร้างเสร็จ
ผมสามารถเขียนโค้ดต่อว่า

SELECT * FROM dta; ถึงตรงนี้ยังใช้ได้ครับ (พอเสร็จจากบรรทัดนี้ dta ก็จะหายไป)
ถ้ามีการเรียกใช้ dta อีก มันก็จะหาไม่เจอแล้ว (บางคนคิดว่าไม่มีประโยชน์ จริงๆแล้วมันมีนะครับ มันสามารถทำให้การเขียนโค้ดเป็น Cascading Style ได้)

อ้ออ ลืมบอกไป
ผมมี Naming Convention ตรงการตั้งชื่อตัวแปร table ที่สร้างขึ้น
ผมจะเขียนเป็น dta ย่อมาจาก "Derived Table A"
ถ้ามีการสร้าง table ขึ้นมาอีก ผมก็จะรันมันไปเรื่อย
dta, dtb, dtc ...

ง่ายดีมั้ยครับ ฮ่าๆๆ 

ต่อไปก็ตรงใจความสำคัญเลย (ซึ่งผมก็ไม่รู้จะเขียนอธิบายยังไงเหมือนกัน)
เอาเป็นว่า ใช้หลักการ ตั้ง start_date ให้เป็น 0 แล้วมันก็จะสามารถหาร HOUR_INTERVAL ได้โดยง่าย ...(เอาเป็นว่า ลองทำความเข้าใจดูเอาเองละกันนะครับ ฮ่าๆๆ)

และผลลัพธ์ก็จะได้ดังนี้
ใครมีวิธีที่ดีกว่านี้ แบ่งปันกันนะครับ