2010-04-26 74 views
12

Mục tiêu của tôi là lấy mức trung bình có trọng số từ một bảng, dựa trên một khóa chính của bảng khác.Tính trung bình có trọng số với LINQ

Ví dụ dữ liệu:

Table1

Key  WEIGHTED_AVERAGE 

0200 0 

Table2

ForeignKey Length Value 
0200   105  52 
0200   105  60 
0200   105  54 
0200   105  -1 
0200   47  55 

tôi cần để có được một bình quân gia quyền dựa vào độ dài của một phân đoạn và tôi cần phải bỏ qua các giá trị là -1 . Tôi biết làm thế nào để làm điều này trong SQL, nhưng mục tiêu của tôi là làm điều này trong LINQ. Nó trông giống như thế này trong SQL:

SELECT Sum(t2.Value*t2.Length)/Sum(t2.Length) AS WEIGHTED_AVERAGE 
FROM Table1 t1, Table2 t2 
WHERE t2.Value <> -1 
AND t2.ForeignKey = t1.Key; 

Tôi vẫn còn khá mới đối với LINQ, và có một thời gian khó khăn để tìm ra cách dịch. Kết quả trọng số trung bình nên đi ra khoảng 55,3. Cảm ơn bạn.

Trả lời

33

tôi làm đủ này mà tôi đã tạo ra một phương pháp mở rộng cho LINQ.

public static double WeightedAverage<T>(this IEnumerable<T> records, Func<T, double> value, Func<T, double> weight) 
{ 
    double weightedValueSum = records.Sum(x => value(x) * weight(x)); 
    double weightSum = records.Sum(x => weight(x)); 

    if (weightSum != 0) 
     return weightedValueSum/weightSum; 
    else 
     throw new DivideByZeroException("Your message here"); 
} 

Sau khi bạn nhận được tập hợp con dữ liệu, cuộc gọi sẽ như sau.

double weightedAverage = records.WeightedAverage(x => x.Value, x => x.Length); 

Điều này trở nên cực kỳ tiện dụng vì tôi có thể nhận được trọng số trung bình của bất kỳ nhóm dữ liệu nào dựa trên một trường khác trong cùng một bản ghi.

Cập nhật

bây giờ tôi kiểm tra xem có chia cho zero và ném một ngoại lệ chi tiết hơn thay vì trả lại 0. Cho phép người sử dụng để nắm bắt những ngoại lệ và xử lý khi cần thiết.

+1

Cảm ơn, rất hữu ích. Tôi vết thương lên làm cho điều này một lớp lót ... công cộng tĩnh float WeightedAverage (IEnumerable mục này, Func giá trị, Func trọng lượng) { các mặt hàng hoàn trả.Sum (item => value (item) * weight (item))/items.Sum (trọng lượng); } – josefresno

+2

Tôi phải thêm "Nếu weightedSum.AlmostZero() trả về 0" sau khi tính toán để bảo vệ chống phân chia bằng không khi tất cả trọng số và hoặc tất cả các giá trị bằng không. AlmostZero là một chức năng mở rộng kiểm tra nếu một đôi là số không. – derdo

4

Nếu bạn chắc chắn rằng đối với mỗi khóa ngoại trong Bảng2 có một bản ghi tương ứng trong Bảng 1, thì bạn có thể tránh tham gia chỉ cần tạo nhóm theo.

Trong trường hợp đó, các truy vấn LINQ là như thế này:

IEnumerable<int> wheighted_averages = 
    from record in Table2 
    where record.PCR != -1 
    group record by record.ForeignKey into bucket 
    select bucket.Sum(record => record.PCR * record.Length)/
     bucket.Sum(record => record.Length); 

CẬP NHẬT

Đây là cách bạn có thể nhận được wheighted_average cho một cụ thể foreign_key.

IEnumerable<Record> records = 
    (from record in Table2 
    where record.ForeignKey == foreign_key 
    where record.PCR != -1 
    select record).ToList(); 
int wheighted_average = records.Sum(record => record.PCR * record.Length)/
    records.Sum(record => record.Length); 

Phương thức ToList được gọi khi tìm nạp hồ sơ, là tránh thực hiện truy vấn hai lần khi tổng hợp các bản ghi trong hai hoạt động riêng biệt.

+0

Điều này trả về một giá trị cho mỗi khoá ngoại khác nhau. Nếu bạn muốn chỉ có trung bình wheighted cho một cụ thể và chỉ ForeignKey, sau đó bạn có thể tránh GroupBy, và chỉ lọc các hồ sơ với các phím nước ngoài mong muốn, và thực hiện các hoạt động tập hợp sau đó. Tôi sẽ chỉnh sửa câu trả lời của tôi để cho bạn thấy làm thế nào. – Fede

1

(trả lời bình luận jsmith để câu trả lời ở trên)

Nếu bạn không muốn để lướt qua một số bộ sưu tập, bạn có thể thử như sau:

var filteredList = Table2.Where(x => x.PCR != -1) 
.Join(Table1, x => x.ForeignKey, y => y.Key, (x, y) => new { x.PCR, x.Length }); 

int weightedAvg = filteredList.Sum(x => x.PCR * x.Length) 
    /filteredList.Sum(x => x.Length); 
+0

Để bạn biết, giải pháp của tôi giả định rằng bạn muốn tính toán mức trung bình có trọng số trên một tập hợp các hàng có khóa ngoại khớp với giá trị khóa của bất kỳ hàng nào trong bảng đầu tiên. Giải pháp của Fede sẽ giúp bạn có được các hàng cho một khóa ngoài cụ thể. Vì vậy, cảm thấy tự do để chọn giải pháp nào phù hợp hơn. –

Các vấn đề liên quan