iT邦幫忙

1

C# WINFROM 從CSV檔讀取資料做分類

想請問各位大大,有一個一百多萬筆的股票資料

資料分別有:交易日期,股票ID,股票名字,卷商ID,卷商名字,價錢,買數,賣數

例:20131210,7894,台積電,551E,第一新竹,205.5,2,0
20131210,7894,台積電,5214K,第一台北,206.5,3,2
20131210,7893,洋華,524K,元大台北,100.5,3,0
20131210,7893,洋華,524K1,元大高雄,99.5,1,5
請問有甚麼方法能夠存取並快速取用這些資料呢?
例:快速取的所有股票ID:台積電的所有資料

圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中
3
小碼農米爾
iT邦高手 1 級 ‧ 2020-07-07 22:32:47
最佳解答

測了一下 100 萬筆資料
LINQ + Where 可以在 1 秒內處理完
所以改用 1000 萬筆測試


產生資料

class Program
{
    static void Main(string[] args)
    {
        using var fs = new FileStream(@"test.csv", FileMode.OpenOrCreate);
        using var sw = new StreamWriter(fs);
        sw.WriteLine("交易日期,股票ID,股票名字,卷商ID,卷商名字,價錢,買數,賣數");
        for (var i = 0; i < 10000000; i++)
            sw.WriteLine(
                $"20131210,7894,台積電{i / 5},551E,第一新竹,205.5,2,0");
    }
}

方法1 - 使用 Where

class Program
{
    static void Main(string[] args)
    {
        //耗時: 1毫秒
        var list = File.ReadLines(@"test.csv")
            .Skip(1)
            .Select(it => it.Split(','));
        
        //耗時: 5秒
        var data = list.Where(it => it[2] == "台積電10500")
            .ToList();
        
        //耗時: 5秒
        var data2 = list.Where(it => it[2] == "台積電25000")
            .ToList();
    }
}

方法2 - 使用 Dictionary

class Program
{
    static void Main(string[] args)
    {
        //耗時: 29秒
        var dict = File.ReadLines(@"test.csv")
            .Skip(1)
            .Select(it => it.Split(','))
            .GroupBy(it => it[2])
            .ToDictionary(it => it.Key);
        
        //耗時: 1毫秒
        var data = dict["台積電10500"].ToList();
        
        //耗時: 1毫秒
        var data2 = dict["台積電25000"].ToList();
    }
}

方法3 - 使用 Lookup

class Program
{
    static void Main(string[] args)
    {
        //耗時: 27秒
        var lookup = File.ReadLines(@"test.csv")
            .Skip(1)
            .Select(it => it.Split(','))
            .ToLookup(it => it[2]);
        
        //耗時: 1毫秒
        var data = lookup["台積電10500"].ToList();
        
        //耗時: 1毫秒
        var data2 = lookup["台積電25000"].ToList();
    }
}

總結

100 萬筆資料使用一般的 Where 就足夠應付

假設資料到達 1000 萬級數
則要看需不需要每次查詢都重新載入資料

如果需要的話還是使用 Where 最合適
雖然每次查詢都要 5 秒
但比起製作 Dictionary 的時間還是少很多

如果資料只需載入一次
使用 Dictionary 或 Lookup 更為合適
雖然載入時間長,但後續查詢都可以在 1 毫秒內完成

Dictionary 因為 Key 只能有一個
所以需要多做一次 GroupBy
Lookup 則可以一個步驟就達成上面兩個步驟的效果
會快一點點,不過沒有差多少
選一個喜歡的就可以

如果查詢條件不固定
就不建議使用 Dictionary 或 Lookup
因為字典只能擁有一個 Key 值

qwe891107 iT邦新手 5 級 ‧ 2020-07-08 08:47:45 檢舉

謝謝大大很詳細!!

1
海綿寶寶
iT邦大神 1 級 ‧ 2020-07-07 11:47:26

下指令很快
要跑多久我就不知道了

find "台積電" data.csv > tsmc.csv
2
japhenchen
iT邦超人 1 級 ‧ 2020-07-07 11:53:01

用 File.ReadAllLines() 把 CSV讀進string[]裡

再用 Split用逗號把每個欄位資料取出

using System;
using System.IO;

namespace testtime
{
    class Program
    {
        static void Main(string[] args)
        {
            string[] alist = File.ReadAllLines("test.csv");
            foreach(string l in alist)
            {
                string[] cells = l.Split(new char[',']);
                if(cells[2].Trim() == "台積電")
                {
                    Console.WriteLine(cells); // 你自己懂得後續怎麼做了
                }
            }
        }
    }
}

看更多先前的回應...收起先前的回應...

還不夠快嗎?可以考慮再做class來parseCSV,但這對於你現在的狀況而言,有點遠

qwe891107 iT邦新手 5 級 ‧ 2020-07-07 15:26:28 檢舉

大哥你好:
我現在是把資料ReadLine進LIST,再從裡面FOREACH或LINQ撈要的資料做計算然後輸出datagriview。
但如果要撈全部的股票的話會跑很慢,所以想說有沒有可以快速取資料的方式來取代FOREACH或LINQ的做法,或是把範圍縮小再FOREACH或LINQ來節省時間。by菜鳥助工

qwe891107
宣告一個CLASS,才好用LINQ

using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;

namespace testtime
{
    class Program
    {
        static void Main(string[] args)
        {
            string[] alist = File.ReadAllLines("test,csv");
            var CSV = new List<CSVLine>();
            foreach (string l in alist)
                CSV.Add(new CSVLine(l));

            //印出所有台積電的資料 
            foreach (var l in CSV.Where(p => p.coName == "台積電"))
            {
                Console.WriteLine(l.coID, l.coName, l.data[0]);  // ..   
            }
        }
    }
    class CSVLine
    {
        public DateTime dt { get; set; }
        public string coID { get; set; }
        public string coName { get; set; }
        public List<string> data { get; set; }
        public CSVLine(string line)
        {
            data=new List<string>();
            string[] detail = line.Split(new char[',']);
            dt = Convert.ToDateTime(detail[0]);
            coID = detail[1];
            coName = detail[2];
            data.AddRange(detail.Skip(3)); // 跳過前三個元素不要
        }
    }

}

qwe891107 iT邦新手 5 級 ‧ 2020-07-07 16:21:54 檢舉

已經有宣告CLASS 並用LIST< CLASS > 存取了

那應該不會太慢才對....除非是幾十萬行....那還是建議存到資料庫才來篩比較好

如果還是慢,那我會建議用PYTHON來處理LIST或DICT會相對快的多一些..

qwe891107 iT邦新手 5 級 ‧ 2020-07-08 08:45:11 檢舉

好的謝謝大大

1
w4560000
iT邦研究生 5 級 ‧ 2020-07-07 17:30:52

改這樣試試看,先宣告成IEnumerable,最後條件查完在ToList出來

private static void Main(string[] args)
{
   // 取得資料
   IEnumerable<Stock> alist =  File.ReadLines("stockTest.csv",System.Text.Encoding.GetEncoding("Big5"))
    .GroupBy(x => x.Split(','))
    .Select(s => new Stock()
    {
       Date = DateTime.ParseExact(s.Key[0], "yyyyMMdd", null),
       StockId = s.Key[1],
       StockName = s.Key[2]
    });

   var a = alist.Where(x => x.StockName == "台積電").ToList();
}

public class Stock
{
   public DateTime Date { get; set; }
   public string StockId { get; set; }
   public string StockName { get; set; }
}

https://ithelp.ithome.com.tw/upload/images/20200707/20110125JonTdQO7Qa.png

qwe891107 iT邦新手 5 級 ‧ 2020-07-08 08:48:33 檢舉

好 我會試試看 謝謝大大

我要發表回答

立即登入回答