Pandas에서 다양한 간격으로 편향된 하나의 열을 다른 열과 그룹화하는 방법은 무엇입니까?

Dec 12 2020

다음 pd.DataFrame이 있습니다.

source = pd.DataFrame([[0.99, 0.98, 0.93, 0.81, 0.85, 0.71, 0.7, 0.69, 0.68, 0.66], 
              [100, 12, 312, 23, 2, 12, 32, 21, 21, 21]]).T

가능한 한 빨리 다음과 같이 변환하고 싶습니다.

desired_result = pd.DataFrame([[0.9, 0.8, 0.7, 0.6], [424, 25, 44, 63]]).T

I가 간격 위에서 정의 여기서 0.1I는 컬럼에 적용되는 0받는 sourcedataframe과 합 1동일한 dataframe의 열. 아이디어는 이것이 다른 간격으로 작동해야한다는 것입니다.

내가 시도한 것 :

  1. 나는 사용에 대해 생각 pd.cut했지만 내가 찾고있는 것이 아닌 것 같습니다.

  2. 나는에 새로운 열을 추가하는 경우 알고 source해당 행에 0.9, 0.8, 0.7 및 0.6의 중복 값 인을 그때 사용할 수있는 groupby다음이 새로운 칼럼에 sum있지만, 깨끗하고 빠른 방법이 있는지 궁금 이것을하기 위해? 예를 들어 다음과 같이 smth :

interval = 0.1
source['ints'] = (source[0] / interval).astype(int)
result = source.groupby(source['ints']).sum().reset_index()
result

그러나 예를 들어 간격 형식을 0.1에서 0.05로 변경하면 위의 방법이 작동하지 않습니다.

어떤 도움을 주시면 감사하겠습니다.

답변

3 PierreD Dec 12 2020 at 21:28

속도 : 항상 가능한 모든 것을 벡터화 하고 가능한 한 많이 apply 하십시오.

다음은 더 빠른 방법입니다 (@DavidErickson에 대한 크레딧 sort=False).

interval = 0.1
source.groupby(np.trunc(source[0] / interval) * interval, sort=False)[1].sum().reset_index()
# out:
     0      1
0  0.9  424.0
1  0.8   25.0
2  0.7   12.0
3  0.6   95.0

큰 경우 속도 차이가 상당히 클 수 있습니다 df.

10,000 개의 빈으로 그룹화 된 1 백만 개의 행을 사용해보십시오.

source = pd.DataFrame(np.random.normal(scale=1000, size=(int(1e6), 2)))

%%timeit
# ... (as above)
26.7 ms ± 292 µs per loop (mean ± std. dev. of 7 runs, 10 loops each)

apply대신 :

1.51 s ± 11 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)

(50 배 느림).

1 DavidErickson Dec 12 2020 at 20:55

custom_round3 가지 수정 한 기능을 사용할 수 있습니다.https://stackoverflow.com/a/40372261/6366770:

  1. 내려 가고 싶은대로 np.floor대신 사용 round했습니다.
  2. 내가 추가 할 수 있도록하는 빈의 "경계"에있는 값까지이 혼잡은 + base/100(그렇게 0.90.9 + .009 = 0.909이 아래로 제대로 바로 국경 원 이상 그래서, 및 0.9 대신 잘못에 0.8 라운드 아래). 나는 이것이 당신을 덮을 것이라고 생각합니다. 1 / 1000안전을 위해 할 수 있습니다.
  3. 내가 공유하는 대답은 찾고 int있었으므로 제거되었습니다 int.

source = pd.DataFrame(np.random.normal(scale=1000, size=(int(1e6), 2)))

def custom_round(x, y, base):
    return source.groupby((base * np.floor((x + (base / 100)) / base)), sort=False)[y].sum()


%timeit custom_round(source[0], 1, .1)
89.8 ms ± 1.14 ms per loop (mean ± std. dev. of 7 runs, 10 loops each)

내 컴퓨터에서 허용되는 답변이 느립니다.

102 ms ± 1.86 ms per loop (mean ± std. dev. of 7 runs, 10 loops each)