<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          『每周譯Go』Go 應用于數(shù)據(jù)科學的案例分享:付多少小費

          共 16748字,需瀏覽 34分鐘

           ·

          2021-08-10 00:01

          提出問題

          當處理數(shù)據(jù)科學難題時,你總會以一個你想要回答的問題開始。這個問題將會影響你選擇數(shù)據(jù),探索過程以及解釋結果。

          本文的問題是:你應該給出租車司機多少(按百分比)小費?

          為了回答這個問題,我們將使用紐約市出租車數(shù)據(jù)集的一部分,使用的數(shù)據(jù)文件是taxi-01-2020-sample.csv.bz2

          注意: CSV 是一種十分令人討厭的格式。它沒有標準,沒有模式,所有內容都被解釋為文本(與 JSON 不同)。如果可以,請選擇其他格式。我首選的數(shù)據(jù)存儲格式是 SQLite 。

          探索過程的代碼

          我們正在尋找問題的答案,我們將專注于快速實現(xiàn)。如果以后將此代碼投入到生產環(huán)境下,那么繼續(xù)重構它。

          為了簡化輸入的工作,我們將在標準輸入中傳遞輸入文件。我們將有幾個探索數(shù)據(jù)的階段,每個階段都有一個相應的命令行開關。在 main 函數(shù)中,我們有以下行:

          r := bzip2.NewReader(os.Stdin)

          并且,我們在每個探索步驟都會調用到 r

          初探

          在開始處理數(shù)據(jù)之前,快速查看它是否符合你的期望。此外,你還應該檢查數(shù)據(jù)是否適合放入內存。

          步驟 1:初次查看


          19 func firstLook(r io.Reader) error {
          20     var numLines, numBytes int
          21     s := bufio.NewScanner(r)
          22     for s.Scan() {
          23         if numLines < 5 {
          24             fmt.Println(s.Text())
          25         }
          26         numBytes += len(s.Text())
          27         numLines++
          28     }
          29 
          30     if err := s.Err(); err != nil {
          31         return err
          32     }
          33 
          34     fmt.Printf("size: %.2fMB\n", float64(numBytes)/1_000_000)
          35     fmt.Printf("lines: %d\n", numLines)
          36     return nil
          37 }

          步驟 1 顯示了對數(shù)據(jù)的初步了解。在第 21 行,我們創(chuàng)建了一個 bufio.Scanner 用以逐行掃描。在第 23-25 行,我們打印文件的前 5 行。在第 34 行,我們打印文件大小,在第 35 行,我們打印了行數(shù)。

          步驟 2: 運行代碼

          $ go run taxi.go -first_look < taxi-01-2020-sample.csv.bz2
          VendorID,tpep_pickup_datetime,tpep_dropoff_datetime,passenger_count,trip_distance,RatecodeID,store_and_fwd_flag,PULocationID,DOLocationID,payment_type,fare_amount,extra,mta_tax,tip_amount,tolls_amount,improvement_surcharge,total_amount,congestion_surcharge
          2,2003-01-01 00:07:17,2003-01-01 14:16:59,1.0,0.0,1.0,N,193,193,2.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0
          2,2008-12-31 23:35:00,2008-12-31 23:36:53,1.0,0.42,1.0,N,263,263,2.0,3.5,0.5,0.5,0.0,0.0,0.3,7.3,2.5
          2,2009-01-01 00:06:19,2009-01-01 00:10:22,1.0,0.85,1.0,N,107,137,2.0,5.0,0.0,0.5,0.0,0.0,0.3,8.3,2.5
          2,2009-01-01 00:48:28,2009-01-01 00:57:48,1.0,0.93,1.0,N,100,186,2.0,7.5,0.0,0.5,0.0,0.0,0.3,10.8,2.5
          size: 101.68MB
          lines: 1000001

          步驟 2 展示了如何運行第一步。我們用 go run 來運行代碼。在輸出中,我們看到文件的前 5 行以及未壓縮文件的大小和行數(shù)。

          該文件是一個 CSV 文件,小到可以放入內存。要計算小費百分比,我們只需要兩列:tip_amounttotal_amount。如果您對數(shù)據(jù)模式感到好奇,請參閱此處。

          加載數(shù)據(jù)

          一旦初次查看的結果與您的假設一致,您就可以加載數(shù)據(jù)。我們將使用 github.com/jszwec/csvutil 解析 CSV 和 gonum 來計算一些統(tǒng)計信息。

          步驟 3: 依賴引入

          14     "github.com/jszwec/csvutil"
          15     "gonum.org/v1/gonum/floats"
          16     "gonum.org/v1/gonum/stat"

          步驟 3 展示了代碼中的外部依賴導入。在第 14 行,我們導入 csvutil ,在第 15-16 行,我們從gonum 導入floatsstat。

          步驟 4: 加載數(shù)據(jù)


          62 type Row struct {
          63     Tip   float64 `csv:"tip_amount"`
          64     Total float64 `csv:"total_amount"`
          65 }
          66 
          67 func loadData(r io.Reader) ([]float64, []float64, error) {
          68     var tip, total []float64
          69     dec, err := csvutil.NewDecoder(csv.NewReader(r))
          70     if err != nil {
          71         return nil, nil, err
          72     }
          73 
          74     for {
          75         var row Row
          76         err := dec.Decode(&row)
          77 
          78         if err == io.EOF {
          79             break
          80         }
          81 
          82         if err != nil {
          83             return nil, nil, err
          84         }
          85 
          86         tip = append(tip, row.Tip)
          87         total = append(total, row.Total)
          88     }
          89 
          90     return tip, total, nil
          91 }

          步驟 4 顯示了我們如何加載數(shù)據(jù)。在第 62-65 行,我們定義了一個 Row 結構體來包含我們感興趣的字段。在第 68 行,我們定義了 tipamount 切片來保存 CSV 中 tip_amounttotal_amount 字段的值。在第 74-88 行,我們運行一個 for 循環(huán)來上傳數(shù)據(jù)。最后在第 90 行,進行數(shù)據(jù)返回。

          步驟 5: 統(tǒng)計


          39 func statistics(r io.Reader) error {
          40     tip, total, err := loadData(r)
          41     if err != nil {
          42         return err
          43     }
          44 
          45     fmt.Printf(
          46         "tip: min=%.2f, mean=%.2f, max=%.2f\n",
          47         floats.Min(tip),
          48         stat.Mean(tip, nil),
          49         floats.Max(tip),
          50     )
          51 
          52     fmt.Printf(
          53         "total: min=%.2f, mean=%.2f, max=%.2f\n",
          54         floats.Min(total),
          55         stat.Mean(total, nil),
          56         floats.Max(total),
          57     )
          58 
          59     return nil
          60 }

          步驟 5 顯示了 statistics 我們數(shù)據(jù)探索的步驟。在第 40 行,我們加載日期。在第 45-50 行,我們打印了小費的最小值、平均值(平均值)和最大值。在第 52-57 行,我們對總數(shù)執(zhí)行相同的操作。

          步驟 6: 運行統(tǒng)計代碼

          $ go run taxi.go -stats < taxi-01-2020-sample.csv.bz2 
          tip: min=-11.80, mean=2.21, max=333.50
          total: min=-333.30, mean=18.47, max=4268.30

          步驟 6 顯示了如何運行統(tǒng)計步驟的代碼。我們可以看到有一些不好的值。兩個最小值都是負數(shù)并且總金額的最大值超過 4,000 美元。

          在任何現(xiàn)實生活中的數(shù)據(jù)集中,都會有錯誤的值,你需要決定如何處理它們。我們將采用簡單的方法并忽略它們。我們將過濾掉負值。此外,由于我們不打算乘坐費用超過 100 美元的出租車,因此我們將過濾掉total_amount大于 100 的行。

          小費計算

          步驟 7: 加載過濾數(shù)據(jù)


          114 func loadDataFiltered(r io.Reader) ([]float64, []float64, error) {
          115     var tip, total []float64
          116     dec, err := csvutil.NewDecoder(csv.NewReader(r))
          117     if err != nil {
          118         return nil, nil, err
          119     }
          120 
          121     for {
          122         var row Row
          123 
          124         err := dec.Decode(&row)
          125         if err == io.EOF {
          126             break
          127         }
          128 
          129         if err != nil {
          130             return nil, nil, err
          131         }
          132 
          133         if row.Total <= 0 || row.Tip <= 0 || row.Total > 100 {
          134             continue
          135         }
          136 
          137         tip = append(tip, row.Tip)
          138         total = append(total, row.Total)
          139     }
          140 
          141     return tip, total, nil
          142 }

          步驟 7 展示了對過濾數(shù)據(jù)的加載。和 loadData 唯一的區(qū)別是第 133-135 行的過濾操作。

          現(xiàn)在我們可以計算我們想要支付的小費。我們希望保持慷慨,因此我們將使用 75% 的分位數(shù)值。75% 分位數(shù)(或百分位數(shù))是 75% 的值低于它的數(shù)字。

          步驟 8: 期待支出的小費


          93  func desiredTip(r io.Reader) error {
          94      tip, total, err := loadDataFiltered(r)
          95      if err != nil {
          96          return err
          97      }
          98  
          99      fmt.Printf("%d filtered values\n", len(tip))
          100 
          101     pct := make([]float64, len(tip))
          102     for i, t := range tip {
          103         pct[i] = t / (total[i] - t)
          104     }
          105 
          106     // stat.Quantile required sorted values
          107     sort.Float64s(pct)
          108     q := 0.75
          109     val := stat.Quantile(q, stat.Empirical, pct, nil)
          110     fmt.Printf("%.2f quantile tip: %.2f\n", q, val)
          111     return nil
          112 }

          步驟 8 展示了 desiredTip 函數(shù)。在第 94 行,我們加載了過濾后的數(shù)據(jù)。在第 99 行,我們打印了過濾后的行數(shù),這樣方便我們檢查不會過濾掉太多行。在第 101-104 行,我們創(chuàng)建了一個百分比切片。最后在第 107-110 行,我們計算 75% 的百分位數(shù),并在第 110 行,我們把它打印了出來。

          步驟 9: 運行代碼

          $ go run taxi.go -tip < taxi-01-2020-sample.csv.bz2 
          716422 filtered values
          0.75 quantile tip: 0.20

          步驟 9 展示了tip步驟輸出。我們看到我們過濾掉了大約 30% 的行。最后,我們看到 75% 的分位數(shù)是 20%。

          可是,等等!也許我們會在周末多給點小費?我們來看一下:

          步驟 10: 加載攜帶時間的數(shù)據(jù)

          145 func unmarshalTime(data []byte, t *time.Time) error {
          146     var err error
          147     *t, err = time.Parse("2006-01-02 15:04:05", string(data))
          148     return err
          149 }
          150 
          151 type TimeRow struct {
          152     Tip   float64   `csv:"tip_amount"`
          153     Total float64   `csv:"total_amount"`
          154     Time  time.Time `csv:"tpep_pickup_datetime"`
          155 }
          156 
          157 func loadDataWithTime(r io.Reader) ([]time.Time, []float64, []float64, error) {
          158     var tip, total []float64
          159     var times []time.Time
          160     dec, err := csvutil.NewDecoder(csv.NewReader(r))
          161     dec.Register(unmarshalTime)
          162     if err != nil {
          163         return nil, nil, nil, err
          164     }
          165 
          166     for {
          167         var row TimeRow
          168 
          169         err := dec.Decode(&row)
          170         if err == io.EOF {
          171             break
          172         }
          173 
          174         if err != nil {
          175             return nil, nil, nil, err
          176         }
          177 
          178         if row.Total <= 0 || row.Tip <= 0 || row.Total > 100 {
          179             continue
          180         }
          181 
          182         tip = append(tip, row.Tip)
          183         total = append(total, row.Total)
          184         times = append(times, row.Time)
          185     }
          186 
          187     return times, tip, total, nil
          188 }

          步驟 10 顯示了如何加載數(shù)據(jù)的時間維度。在第 145-149 行,我們編寫了一個 unmarshalTime 函數(shù)來從[]byte 解析為時間。在第 151-155 行,我們定義 TimeRow 為包含 Time 字段的行。在第 159 行,我們定義了 times 切片,在第 160 行,我們注冊 unmarshalTime 以處理 time.Time 字段。最后在第 187 行,我們返回時間、小費和總數(shù)。

          步驟 11: 按工作日計算小費

          190 func weekdayTip(r io.Reader) error {
          191     times, tip, total, err := loadDataWithTime(r)
          192     if err != nil {
          193         return err
          194     }
          195 
          196     pct := make(map[time.Weekday][]float64)
          197     for i, t := range tip {
          198         wday := times[i].Weekday()
          199         p := t / (total[i] - t)
          200         pct[wday] = append(pct[wday], p)
          201     }
          202 
          203     for wday := time.Sunday; wday < time.Saturday; wday += 1 {
          204         // stat.Quantile required sorted values
          205         p := pct[wday]
          206         sort.Float64s(p)
          207         q := 0.75
          208         val := stat.Quantile(q, stat.Empirical, p, nil)
          209         fmt.Printf("%-10s: %.2f quantile tip: %.2f (%6d samples)\n", wday, q, val, len(p))
          210     }
          211 
          212     return nil
          213 }

          步驟 11 展示了 “工作日小費” 計算。在第 196 行,我們使用字典來保存每個工作日的百分比。在第 197 到 201 行,我們填充每個工作日的百分比,這相當于數(shù)據(jù)庫中的 “GROUP BY” 操作。在第 203-209 行,我們遍歷每個工作日,計算 0.75 分位數(shù)并將其打印出來。

          在第 209 行,我們使用 -10s%讓所有工作日至少占 10 個字符來行對齊。這似乎是一個微不足道的細節(jié),但對齊輸出對于我們來說會更容易比較 - 正如您在下面的輸出中看到的那樣。出于同樣的原因,我們也對齊了樣本數(shù)量。

          步驟 12: 運行代碼

          $ go run taxi.go -daily < taxi-01-2020-sample.csv.bz2
          Sunday    : 0.75 quantile tip: 0.20 ( 77942 samples)
          Monday    : 0.75 quantile tip: 0.20 ( 82561 samples)
          Tuesday   : 0.75 quantile tip: 0.20 ( 97634 samples)
          Wednesday : 0.75 quantile tip: 0.20 (118497 samples)
          Thursday  : 0.75 quantile tip: 0.20 (125692 samples)
          Friday    : 0.75 quantile tip: 0.20 (125743 samples)

          步驟 12 運行了上步驟的代碼,展示了每天的數(shù)據(jù)結果。我們可以看到周末小費的百分比并沒有差異。

          結論

          只需要一點好奇心并會一點 Go 就可以讓您在數(shù)據(jù)科學之旅中走得更遠。您不必使用深度學習、決策樹、支持向量機和其他算法來獲得有用的答案。

          您正如何將 Go 用于數(shù)據(jù)科學?我很想聽聽,請通過 [email protected] 聯(lián)系我。

          • 原文地址:https://www.ardanlabs.com/blog/2021/07/go-data-science-how-much-tip.html

          • 原文作者:Miki Tebeka

          • 本文永久鏈接:https://github.com/gocn/translator/blob/master/2021/w30_Data_Science_in_Go_How_Much_To_Tip.md

          • 譯者:lsj1342

          • 校對:laxiaohong、cvley




          瀏覽 47
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

          分享
          舉報
          評論
          圖片
          表情
          推薦
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

          分享
          舉報
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  中文字幕日屄 | 一级黄色免费在线电影 | 亚洲操小逼 | 爱爱免费视频网址 | 亚洲男女内射在线播放 |