문제

나는 Clojure를 배우고 있으며 관용 사용에 대한 조언을 원합니다. 작은 통계 패키지의 일부로 데이터 세트의 모드를 계산하는 기능이 있습니다. (배경 :이 모드는 일련의 데이터 세트에서 가장 일반적인 값입니다.이를 계산하기위한 거의 12 개의 공개 알고리즘이 있습니다. 여기에 사용 된 것은 Bernard Rosner의 "Biostatistics의 기초"6th Ed에서 나온 것입니다.)

(defn tally-map
 " Create a map where the keys are all of the unique elements in the input
   sequence and the values represent the number of times those elements
   occur. Note that the keys may not be formatted as conventional Clojure
   keys, i.e. a colon preceding a symbol."
  [aseq]
  (apply merge-with + (map (fn [x] {x 1}) aseq)))

(defn mode
 " Calculate the mode. Rosner p. 13. The mode is problematic in that it may
   not be unique and may not exist at all for a particular group of data.
   If there is a single unique mode, it is returned. If there are multiple
   modes, they are returned as a list. If there is no mode, that is all
   elements are present in equal frequency, nil is returned."
  [aseq]
  (let [amap (tally-map aseq)
        mx (apply max (vals amap))
        k (keys amap)
        f (fn [x] (not (nil? x)))
        modes (filter f (map #(if (= mx (get amap %)) %) k))
        ]
    (cond (= 1 (count modes)) (first modes)
      (every? #(= mx %) (vals amap)) nil
      :else modes)
    )
  )

질문이있는 몇 가지 사항이 있습니다.

  1. 논쟁 거리. 함수는 단일 시퀀스를 허용합니다. 추가 함수와 같은 가변 수의 인수를 받아들이는 것이 더 관용적입니까?
  2. 코드 냄새. "let"가 그보다 훨씬 더 복잡한 것처럼 보입니다. 이 방법을 더 간결하게 만드는 언어 나 라이브러리의 명백한 (또는 명백하지 않은) 사용을 놓쳤습니까?

도움을 주셔서 미리 감사드립니다.

도움이 되었습니까?

해결책

내 테이크는 다음과 같습니다.

  1. 다른 핵심 Clojure 기능은 인수로 인수를 취하는 반면 다른 핵심 Clojure 기능은 여러 논증을 취하기 때문에 제 생각에는 진정한 관용적 인 방법이 없습니다. 이미 순서대로 데이터가있는 경우 SEQ를 인수로 사용합니다. 신청을 위해 통화를 절약 할 수 있습니다.

  2. 호출 코드는 항상 반환 값을 사용하기 전에 반환 값을 확인해야하기 때문에 어떤 경우에는 값을 반환하는 함수와 다른 경우 값 목록을 작성하지 않습니다. 대신 나는 단일 모드를 하나의 항목 만있는 seq로 반환합니다. 그러나이 기능을 호출하는 코드에 따라 이유가있을 수 있습니다.

그 외에도 다음과 같은 모드 기능을 다시 작성합니다.

(defn mode [aseq]
  (let [amap (tally-map aseq)
        mx (apply max (vals amap))
        modes (map key (filter #(= mx (val %)) amap))
        c (count modes)]
    (cond
      (= c 1) (first modes)
      (= c (count amap)) nil
      :default modes)))

함수 f를 정의하는 대신 ID 함수를 사용할 수 있습니다 (데이터에 논리적으로 거짓 값이 포함되지 않는 한). 그러나 당신은 그것을 필요로하지 않습니다. 나는 다른 방식으로 모드를 찾습니다. 나에게 더 읽을 수 있습니다. Map AMAP는 일련의 맵 항목 (키-값 쌍) 역할을합니다. 먼저 값 mx를 가진 항목 만 필터링합니다. 그런 다음 키 함수를 매핑하여 일련의 키를 제공합니다.

모드가 있는지 확인하려면지도를 다시 고정하지 않습니다. 대신 모드 수를 맵 항목 수와 비교합니다. 그것들이 같으면 모든 요소는 동일한 주파수를 가지고 있습니다!

다음은 항상 seq를 반환하는 기능입니다.

(defn modes [aseq]
  (let [amap (tally-map aseq)
        mx (apply max (vals amap))
        modes (map key (filter #(= mx (val %)) amap))]
    (when (< (count modes) (count amap)) modes)))

다른 팁

내 생각에, 컬렉션을 통해 일부 기능을 매핑 한 다음 목록을 즉시 하나의 항목으로 압축하는 것은 사용하는 부호입니다. reduce.

(defn tally-map [coll]
  (reduce (fn [h n]
            (assoc h n (inc (h n 0))))
          {} coll))

이 경우 나는 쓸 것이다 mode FN은 당신이했던 것처럼 단일 컬렉션을 논쟁으로 취합니다. 이와 같은 함수에 대해 여러 인수를 사용하려는 유일한 이유는 문자 그대로의 인수를 많이 입력해야한다면.

예를 들어, 이것이 대화식 대체 스크립트를위한 것이고 종종 타이핑을 할 것입니다. (mode [1 2 1 2 3]) 말 그대로, 당신은 기능이 여러 인수를 받아야합니다. [] 함수에서 항상 호출하십시오. 파일에서 많은 숫자를 읽고 해당 숫자의 모드를 가져갈 계획이라면, 함수가 컬렉션 인 단일 인수를 가져 와서 사용하지 못하게 할 수 있도록합니다. apply 항상. 가장 일반적인 사용 사례가 후자라고 생각합니다. 나는 믿는다 apply 또한 수집 인수를받는 기능 호출이있을 때 피하는 오버 헤드를 추가합니다.

나는 당신이 가져야 할 다른 사람들과 동의합니다 mode 결과가 하나만 있더라도 결과 목록을 반환하십시오. 그것은 당신의 삶을 더 쉽게 만들 것입니다. 이름을 바꿀 수도 있습니다 modes 당신이 그것에있는 동안.

다음은 좋은 간결한 구현입니다 mode:

(defn mode [data] 
  (first (last (sort-by second (frequencies data)))))

이것은 다음과 같은 사실을 이용합니다.

  • 그만큼 frequencies 함수 값 -> 주파수의 맵을 반환합니다
  • 맵을 키 값 쌍 시퀀스로 취급 할 수 있습니다.
  • 이 순서를 값으로 정렬하면 (the second 각 쌍의 항목), 그러면 시퀀스의 마지막 항목은 모드를 나타냅니다.

편집하다

다중 모드 케이스를 처리하려면 추가를 삽입 할 수 있습니다. partition-by 모든 값을 최대 주파수로 유지하려면 :

(defn modes [data] 
  (->> data
       frequencies 
       (sort-by second)
       (partition-by second)
       last
       (map first)))

나에게 괜찮아 보인다. 나는 그것을 교체했다

f (fn [x] (not (nil? x)))
mode (filter f (map #(if (= mx (get amap %)) %) k))

~와 함께

mode (remove nil? (map #(if (= mx (get amap %)) %) k))

(왜 그런지 모르겠어요 not-nil? 들어 있지 않습니다 clojure.core; 매일 필요한 것입니다.)

단일 고유 모드가 있으면 반환됩니다. 여러 모드가있는 경우 목록으로 반환됩니다. 모드가 없으면 모든 요소가 동일한 주파수로 존재하며 NIL이 반환됩니다. "

당신은 매번 seq를 단순히 반환하는 것에 대해 생각할 수 있습니다 (하나의 요소 또는 비어있는 것은 괜찮습니다). 그렇지 않으면, 케이스는 호출 코드로 구분되어야합니다. SEQ를 항상 반환함으로써 귀하의 결과는 마술처럼 SEQ를 기대하는 다른 기능에 대한 인수로 작용할 것입니다.

라이센스 : CC-BY-SA ~와 함께 속성
제휴하지 않습니다 StackOverflow
scroll top