한 번의 대량 읽기 데이터 세트의 중앙값을 추정하는 데 유용한 알고리즘은 무엇입니까?


47

저장하기에 너무 큰 데이터 세트의 중앙값을 추정하여 각 값을 한 번만 읽을 수 있도록 (해당 값을 명시 적으로 저장하지 않는 한) 좋은 알고리즘 (최소 계산, 최소 스토리지 요구 사항)을 찾고 있습니다. 추정 할 수있는 데이터에는 한계가 없습니다.

정확도가 알려진 한 근사치가 좋습니다.

어떤 포인터?


4
아마도 Stackoverflow를 요청하면 더 나은 답변을 얻을 수 있습니다.

2
@Srikant :> 통계 분야에서 꽤 활발한 연구 분야입니다. :) 스토리지 측면에서 이론적 인 한계에 가장 가까운 솔루션에는 꽤 영리한 확률 구성도 포함됩니다. 두 달 전에 처음 보았을 때 나는 놀랐습니다. 여기 눈을 맞추는 것보다 더 많은 통계가 있습니다.
user603

답변:


6

각 그룹의 중앙값을 계산 한 경우 데이터 세트를 훨씬 더 작은 데이터 세트 (예 : 100 또는 1000 또는 10,000 데이터 포인트)로 그룹화 할 수 있습니까? 충분한 데이터 세트로이 작업을 수행 한 경우, 더 작은 데이터 세트를 실행하면 '평균'솔루션으로 수렴하여 각 작은 세트 및이 woul의 평균 결과와 같은 것을 그릴 수 있습니다.


이것은 흥미롭고 통계적 조언이 올 수있는 곳입니다! 총 50 만 개의 iid 포인트가 있고 1,000 개의 그룹을보고 각 그룹의 중앙값을 계산한다고 가정합니다. 이제 중간 값이 500입니다. 이 500 개의 중앙값을 기준으로 전체 중앙값의 신뢰 구간을 계산할 수있는 이론이 있습니까?
PeterR

4
그래서 오랫동안 잃어버린 동료에 따르면 가장 좋은 방법은 Chiranjeeb Buragohain과 Subhash Suri 인 것 같습니다. 하천의 Quantiles. cs.ucsb.edu/~suri/psdir/ency.pdf 또한 작은 데이터 세트의 중간 값이 정규 분포로 수렴되므로 중간 값에 대한 conf 간격을 구성 할 수 있으므로 Ian의 접근 방식이 마음에 듭니다 .
PeterR

9

비닝 절차와 같은 것은 어떻습니까? 값이 백만에서 백만 사이임을 아는 것으로 (예시 적으로) 가정하십시오. 크기가 S 인 N 빈을 설정합니다. 따라서 S = 10000 인 경우 값 [1 : 10000, 10001 : 20000, ..., 990001 : 1000000]에 해당하는 100 개의 빈을 갖게됩니다.

그런 다음 값을 단계별로 살펴보십시오. 각 값을 저장하는 대신 적절한 빈에서 카운터를 늘리십시오. 각 구간의 중간 점을 추정값으로 사용하면 중간 값을 합리적으로 근사화 할 수 있습니다. 출력 함의 크기를 변경하여 원하는 해상도로 세밀하게 조정할 수 있습니다. 당신은 당신이 가진 메모리의 양에 의해서만 제한됩니다.

값이 얼마나 큰지 알지 못하므로 빠른 백 오브 백 계산을 사용하여 메모리가 부족하지 않을 정도로 큰 빈 크기를 선택하십시오. 빈이 희박하게 저장되어 빈이 값을 포함하는 경우에만 빈을 추가 할 수 있습니다.

편집하다:

링크 ryfm은 중간 지점을 사용하는 대신 누적 비율을 사용하여 중앙 빈 내의 지점을보다 정확하게 추정하는 추가 단계와 함께이 작업을 수행하는 예를 제공합니다. 이것은 좋은 개선입니다.


비닝 접근 방식의 문제점은 데이터에 대한 상한이 충분하지 않으므로 가장 큰 빈의 중간 점이 커야한다는 것입니다. 따라서 우리는 많은 수의 빈 (메모리가 충분하지 않음)이 필요하거나 꽤 넓은 빈이 있어야합니다 (그러면 상당히 부정확 한 답변으로 이어질 것입니다). 그리고 데이터는 그리 드물지 않습니다.
PeterR

중간 값에만 관심이 있기 때문에 변수의 높은 값에서 구간을 더 넓게 만들 수없는 이유는 무엇입니까?
russellpierce

drknexus-우리는 가장 큰 쓰레기통이 무엇인지 모르기 때문에.
PeterR

당신이 있습니까 어떤 범위가 될 것입니다 무엇으로 직관을? 답의 절반 이상이 숫자 N 미만이 될 것이라고 확신한다면 마지막 빈을 원하는만큼 크게 만들 수 있습니다. 마지막 빈이 1 조보다 큰 숫자 일 수 있습니다. 충분히 높습니까? 최신 시스템의 메모리 양을 사용하면 많은 저장소를 저장하고 상당히 높은 해상도를 얻을 수 있습니다. 데이터 구조 측면에서 우리는 여기서 환상적이고 메모리 집약적 인 것을 말하지 않습니다.
chrisamiller

직감? 예. 그리고 당신의 접근 방식은 일반적으로 효과가 있습니다. 그러나이 경우 많은 메모리 / 계산을 할 수 없습니다. 장치가 초당 수만 개의 항목을 볼 수있는 네트워킹 응용 프로그램에 있으며이 목적으로 처리량이 거의 없습니다. 이상적이고 전형적인 시나리오는 아니지만, 그것이 흥미 롭습니다!
PeterR

9

비슷한 질문에 대한 답변으로 다시 안내합니다 . 요컨대, (정확한) 중앙값을 계산하기 위해 최악의 경우 최악의 복잡성을 가진 '즉석에서'알고리즘을 한 번 읽습니다 .O(n)


8

Rivest에-Tarjan-선택 알고리즘은 당신이 어떤 정렬하지 않고 선형 시간에 중간 요소를 계산하게됩니다 (때로는 중간 - 중 - 중간 값 알고리즘이라고합니다). 큰 데이터 세트의 경우 로그 선형 정렬보다 훨씬 빠릅니다. 그러나 메모리 저장 문제를 해결하지는 못합니다.



2

나는 이것을 할 필요가 없었으므로 이것은 단지 제안 일뿐입니다.

두 가지 다른 가능성이 있습니다.

반 데이터

  1. 반으로 데이터를로드하고 정렬
  2. 다음으로 나머지 값을 읽고 정렬 된 목록과 비교하십시오.
    1. 새 값이 더 크면 버립니다.
    2. 그렇지 않으면 값을 정렬 된 목록에 넣고 해당 목록에서 가장 큰 값을 제거하십시오.

샘플링 분포

다른 옵션은 샘플링 분포와 관련된 근사값을 사용하는 것입니다. 데이터가 보통이면 중간 n 의 표준 오차는 다음과 같습니다.

1.253 * sd / sqrt (n)

n 의 크기를 결정하기 위해 R에서 빠른 Monte-Carlo 시뮬레이션을 실행했습니다.

n = 10000
outside.ci.uni = 0
outside.ci.nor = 0
N=1000
for(i in 1:N){
  #Theoretical median is 0
  uni = runif(n, -10, 10)
  nor  = rnorm(n, 0, 10)

  if(abs(median(uni)) > 1.96*1.253*sd(uni)/sqrt(n))
    outside.ci.uni = outside.ci.uni + 1

  if(abs(median(nor)) > 1.96*1.253*sd(nor)/sqrt(n))
    outside.ci.nor = outside.ci.nor + 1
}

outside.ci.uni/N
outside.ci.nor/N

n = 10000의 경우, 균일 한 중앙값 추정치의 15 %가 CI 외부에있었습니다.


3
데이터 세트가 반으로 읽기에는 너무 클 수 있습니다 ... 프로세싱을 수행하는 장치가 초당 수만 개의 항목을 볼 수있는 네트워킹 컨텍스트에 있으며 아마도 수백 개만 저장할 메모리가 충분합니다. 또한 데이터는 분명히 가우스가 아닙니다. 사실 그것은 일반적인 분포에 잘 맞지 않습니다.
PeterR


1

다음은 질문에 대한 답변에 유래에 질문 있어요 : https://stackoverflow.com/questions/1058813/on-line-iterator-algorithms-for-estimating-statistical-median-mode-skewness/2144754#2144754

반복 업데이트 중앙값 + = eta * sgn (sample-median)은 갈 수있는 것처럼 들립니다.


1
그러나 에타를 선택하는 방법과 통계는 무엇을 의미합니까? 즉,이 결과로부터 중앙값에 대한 신뢰 구간을 형성하는 방법은 무엇입니까?
PeterR

@ PeterR, 당신이 사용한 최종 솔루션은 무엇입니까?
Aakash Goel

1

Remedian 알고리즘 (PDF)는 낮은 저장 요구 사항 및 잘 정의 된 정확도로 원 패스 중앙값을 준다.

기본 b에 대한 교정은 단일 관측치 만 남을 때까지 b 개의 관측치 그룹의 중간 값을 계산 한 다음이 중간 값의 중간 값을 계산하여 진행됩니다. 이 방법은 단지 크기가 b 인 k 개의 배열 만 필요합니다 (n = b ^ k) ...


1

사용 하고있는 이 1 ~ 100000과 같은 특정 범위 내에있는 경우 정수 버킷 (이 코드는 BSD 라이센스를받은 ea에서 가져온 코드)을 사용하여 매우 많은 수의 값 (예 : 수조 개의 항목)에서 중앙값을 효율적으로 계산할 수 있습니다 -utils / sam-stats.cpp)

class ibucket {
public:
    int tot;
    vector<int> dat;
    ibucket(int max) {dat.resize(max+1);tot=0;}
    int size() const {return tot;};

    int operator[] (int n) const {
        assert(n < size());
        int i;
        for (i=0;i<dat.size();++i) {
            if (n < dat[i]) {
                return i;
            }
            n-=dat[i];
        }
    }

    void push(int v) {
        assert(v<dat.size());
        ++dat[v];
        ++tot;
    }
};


template <class vtype>
double quantile(const vtype &vec, double p) {
        int l = vec.size();
        if (!l) return 0;
        double t = ((double)l-1)*p;
        int it = (int) t;
        int v=vec[it];
        if (t > (double)it) {
                return (v + (t-it) * (vec[it+1] - v));
        } else {
                return v;
        }
}

또한 이것은 실시간 중앙값 등을 위해 유한 수의 빈을 사용하는 것으로 확장 될 수 있습니다.
Erik Aronesty
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.