AskOverflow.Dev

AskOverflow.Dev Logo AskOverflow.Dev Logo

AskOverflow.Dev Navigation

  • Início
  • system&network
  • Ubuntu
  • Unix
  • DBA
  • Computer
  • Coding
  • LangChain

Mobile menu

Close
  • Início
  • system&network
    • Recentes
    • Highest score
    • tags
  • Ubuntu
    • Recentes
    • Highest score
    • tags
  • Unix
    • Recentes
    • tags
  • DBA
    • Recentes
    • tags
  • Computer
    • Recentes
    • tags
  • Coding
    • Recentes
    • tags
Início / coding / Perguntas / 77878728
Accepted
FredMaster
FredMaster
Asked: 2024-01-25 17:15:53 +0800 CST2024-01-25 17:15:53 +0800 CST 2024-01-25 17:15:53 +0800 CST

Dataframe Polars: grupos sobrepostos

  • 772

Atualmente estou "convertendo" de pandas para polares porque gosto muito da API. Esta pergunta é uma pergunta mais geral para uma pergunta anterior minha (veja aqui )

Eu tenho o seguinte dataframe


# Dummy data
df = pl.DataFrame({
    "Buy_Signal": [1, 0, 1, 0, 1, 0, 0], 
    "Returns": [0.01, 0.02, 0.03, 0.02, 0.01, 0.00, -0.01],
})

Em última análise, quero fazer agregações na coluna Returnscondicionais a intervalos diferentes - que são dados por column Buy_Signal. No caso acima, o comprimento é de 1 até o final do dataframe. O dataframe resultante deve, portanto, ter a seguinte aparência

| group     | Returns   |
|------:    |--------:  |
|   u32     |     f64   |
|     1     |    0.01   |
|     1     |    0.02   |
|     1     |    0.03   |
|     1     |    0.02   |
|     1     |    0.01   |
|     1     |     0.0   |
|     1     |   -0.01   |
|     2     |    0.03   |
|     2     |    0.02   |
|     2     |    0.01   |
|     2     |     0.0   |
|     2     |   -0.01   |
|     3     |    0.01   |
|     3     |     0.0   |
|     3     |   -0.01   |

Uma abordagem postada como resposta à minha pergunta anterior é a seguinte:


# Build overlapping group index
idx = df.select(index=
          pl.when(pl.col("Buy_Signal") == 1)
          .then(pl.int_ranges(pl.int_range(pl.len()), pl.len()  ))
).explode(pl.col("index")).drop_nulls().cast(pl.UInt32)

# Join index with original data
df = (df.with_row_index()
    .join(idx, on="index")
    .with_columns(group = (pl.col("index") == pl.col("index").max())
                .shift().cum_sum().backward_fill() + 1)
    .select(["group", "Returns"])
)
df

Pergunta: existem outras boas soluções para este problema.

Com bom quero dizer (i) legível e/ou (ii) rápido.

Meu problema real contém conjuntos de dados muito maiores.

Obrigado

python
  • 3 3 respostas
  • 50 Views

3 respostas

  • Voted
  1. Best Answer
    Hericks
    2024-01-25T18:36:41+08:002024-01-25T18:36:41+08:00

    Para completar, aqui está uma solução alternativa que não depende de funcionalidade experimental.

    (
        df
        .with_columns(
            pl.col("Buy_Signal").cum_sum().alias("group")
        )
        .with_columns(
            pl.int_ranges(pl.col("group").min(), pl.col("group")+1)
        )
        .explode("group")
        .sort("group")
    )
    

    Saída.

    shape: (15, 3)
    ┌────────────┬─────────┬───────┐
    │ Buy_Signal ┆ Returns ┆ group │
    │ ---        ┆ ---     ┆ ---   │
    │ i64        ┆ f64     ┆ i64   │
    ╞════════════╪═════════╪═══════╡
    │ 1          ┆ 0.01    ┆ 1     │
    │ 0          ┆ 0.02    ┆ 1     │
    │ 1          ┆ 0.03    ┆ 1     │
    │ 0          ┆ 0.02    ┆ 1     │
    │ 1          ┆ 0.01    ┆ 1     │
    │ …          ┆ …       ┆ …     │
    │ 0          ┆ 0.0     ┆ 2     │
    │ 0          ┆ -0.01   ┆ 2     │
    │ 1          ┆ 0.01    ┆ 3     │
    │ 0          ┆ 0.0     ┆ 3     │
    │ 0          ┆ -0.01   ┆ 3     │
    └────────────┴─────────┴───────┘
    
    • 2
  2. Roman Pekar
    2024-01-25T18:09:23+08:002024-01-25T18:09:23+08:00

    Eu acho que é um bom caso para uso de cumulative_eval().

    À primeira vista, você precisa ter 2 passagens no seu DataFrame. Primeiro, você precisa calcular cum_sumpara acertar o agrupamento:

    df = df.with_columns(
            Holdings = pl.col('Buy_Signal').cum_sum()
        ).group_by("Holdings").agg(
            pl.col(["Returns"])).sort("Holdings", descending=True
        )
    
    print(df)
    
    shape: (3, 2)
    ┌──────────┬────────────────────┐
    │ Holdings ┆ Returns            │
    │ ---      ┆ ---                │
    │ i64      ┆ list[f64]          │
    ╞══════════╪════════════════════╡
    │ 3        ┆ [0.01, 0.0, -0.01] │
    │ 2        ┆ [0.03, 0.02]       │
    │ 1        ┆ [0.01, 0.02]       │
    └──────────┴────────────────────┘
    

    Observe que atualmente nosso DataFrame está classificado com descending=True, isso é importante, pois queremos que nossas listas sejam agregadas do maior grupo para o menor.

    E agora cumulative_eval(), junto com implode():

    df = df.with_columns(
            pl.col("Returns").cumulative_eval(pl.element().explode().implode())
        )
    
    print(df.explode("Returns"))
    
    shape: (15, 2)
    ┌──────────┬─────────┐
    │ Holdings ┆ Returns │
    │ ---      ┆ ---     │
    │ i64      ┆ f64     │
    ╞══════════╪═════════╡
    │ 3        ┆ 0.01    │
    │ 3        ┆ 0.0     │
    │ 3        ┆ -0.01   │
    │ 2        ┆ 0.01    │
    │ …        ┆ …       │
    │ 1        ┆ 0.03    │
    │ 1        ┆ 0.02    │
    │ 1        ┆ 0.01    │
    │ 1        ┆ 0.02    │
    └──────────┴─────────┘
    

    Você pode ver que o resultado corresponde ao que você esperaria. No entanto, você deve ter cuidado com o desempenho, pois há uma observação na cumulative_evaldocumentação:

    Aviso

    Esta funcionalidade é experimental e pode mudar sem ser considerada uma alteração significativa.

    Isso pode ser muito lento, pois pode ter complexidade O(n^2). Não use isto para operações que visitam todos os elementos.

    • 1
  3. jqurious
    2024-01-25T20:14:41+08:002024-01-25T20:14:41+08:00

    Apenas para adicionar mais algumas informações, pois acho que o título da pergunta é baseado na minha redação anterior:

    Encontrei uma definição existente do problema (qual pode ser um título melhor para a pergunta?)

    Um Polars equivalente ao ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING enquadramento de janela do SQL.

    ┌────────────┬─────────┐
    │ Buy_Signal ┆ Returns │
    │ ---        ┆ ---     │
    │ i64        ┆ f64     │ # New "group/window" starts when Buy_Signal == 1
    ╞════════════╪═════════╡ # 
    │ 1          ┆ 0.01    │ # ┌─ group 1 ─┐
    │ 0          ┆ 0.02    │ # │           │ 
    │ 1          ┆ 0.03    │ # │           │┌─ group 2 ─┐
    │ 0          ┆ 0.02    │ # │           ││           │ 
    │ 1          ┆ 0.01    │ # │           ││           │┌─ group 3 ─┐
    │ 0          ┆ 0.0     │ # │           ││           ││           │
    │ 0          ┆ -0.01   │ # └───────────┘└───────────┘└───────────┘
    └────────────┴─────────┘
    

    por exemplo, usando DuckDB para demonstrar:

    duckdb.sql("""
    from df
    select 
       buy_signal, 
       returns, 
       case when buy_signal = 1
       then
          list(returns) over (rows between current row and unbounded following)
       end as group
    """)
    
    ┌────────────┬─────────┬────────────────────────────────────────────┐
    │ Buy_Signal │ Returns │                   group                    │
    │   int64    │ double  │                  double[]                  │
    ├────────────┼─────────┼────────────────────────────────────────────┤
    │          1 │    0.01 │ [0.01, 0.02, 0.03, 0.02, 0.01, 0.0, -0.01] │
    │          0 │    0.02 │ NULL                                       │
    │          1 │    0.03 │ [0.03, 0.02, 0.01, 0.0, -0.01]             │
    │          0 │    0.02 │ NULL                                       │
    │          1 │    0.01 │ [0.01, 0.0, -0.01]                         │
    │          0 │     0.0 │ NULL                                       │
    │          0 │   -0.01 │ NULL                                       │
    └────────────┴─────────┴────────────────────────────────────────────┘
    
    • 0

relate perguntas

  • Como divido o loop for em 3 quadros de dados individuais?

  • Como verificar se todas as colunas flutuantes em um Pandas DataFrame são aproximadamente iguais ou próximas

  • Como funciona o "load_dataset", já que não está detectando arquivos de exemplo?

  • Por que a comparação de string pandas.eval() retorna False

  • Python tkinter/ ttkboostrap dateentry não funciona quando no estado somente leitura

Sidebar

Stats

  • Perguntas 205573
  • respostas 270741
  • best respostas 135370
  • utilizador 68524
  • Highest score
  • respostas
  • Marko Smith

    Vue 3: Erro na criação "Identificador esperado, mas encontrado 'import'" [duplicado]

    • 1 respostas
  • Marko Smith

    Por que esse código Java simples e pequeno roda 30x mais rápido em todas as JVMs Graal, mas não em nenhuma JVM Oracle?

    • 1 respostas
  • Marko Smith

    Qual é o propósito de `enum class` com um tipo subjacente especificado, mas sem enumeradores?

    • 1 respostas
  • Marko Smith

    Como faço para corrigir um erro MODULE_NOT_FOUND para um módulo que não importei manualmente?

    • 6 respostas
  • Marko Smith

    `(expression, lvalue) = rvalue` é uma atribuição válida em C ou C++? Por que alguns compiladores aceitam/rejeitam isso?

    • 3 respostas
  • Marko Smith

    Quando devo usar um std::inplace_vector em vez de um std::vector?

    • 3 respostas
  • Marko Smith

    Um programa vazio que não faz nada em C++ precisa de um heap de 204 KB, mas não em C

    • 1 respostas
  • Marko Smith

    PowerBI atualmente quebrado com BigQuery: problema de driver Simba com atualização do Windows

    • 2 respostas
  • Marko Smith

    AdMob: MobileAds.initialize() - "java.lang.Integer não pode ser convertido em java.lang.String" para alguns dispositivos

    • 1 respostas
  • Marko Smith

    Estou tentando fazer o jogo pacman usando apenas o módulo Turtle Random e Math

    • 1 respostas
  • Martin Hope
    Aleksandr Dubinsky Por que a correspondência de padrões com o switch no InetAddress falha com 'não cobre todos os valores de entrada possíveis'? 2024-12-23 06:56:21 +0800 CST
  • Martin Hope
    Phillip Borge Por que esse código Java simples e pequeno roda 30x mais rápido em todas as JVMs Graal, mas não em nenhuma JVM Oracle? 2024-12-12 20:46:46 +0800 CST
  • Martin Hope
    Oodini Qual é o propósito de `enum class` com um tipo subjacente especificado, mas sem enumeradores? 2024-12-12 06:27:11 +0800 CST
  • Martin Hope
    sleeptightAnsiC `(expression, lvalue) = rvalue` é uma atribuição válida em C ou C++? Por que alguns compiladores aceitam/rejeitam isso? 2024-11-09 07:18:53 +0800 CST
  • Martin Hope
    The Mad Gamer Quando devo usar um std::inplace_vector em vez de um std::vector? 2024-10-29 23:01:00 +0800 CST
  • Martin Hope
    Chad Feller O ponto e vírgula agora é opcional em condicionais bash com [[ .. ]] na versão 5.2? 2024-10-21 05:50:33 +0800 CST
  • Martin Hope
    Wrench Por que um traço duplo (--) faz com que esta cláusula MariaDB seja avaliada como verdadeira? 2024-05-05 13:37:20 +0800 CST
  • Martin Hope
    Waket Zheng Por que `dict(id=1, **{'id': 2})` às vezes gera `KeyError: 'id'` em vez de um TypeError? 2024-05-04 14:19:19 +0800 CST
  • Martin Hope
    user924 AdMob: MobileAds.initialize() - "java.lang.Integer não pode ser convertido em java.lang.String" para alguns dispositivos 2024-03-20 03:12:31 +0800 CST
  • Martin Hope
    MarkB Por que o GCC gera código que executa condicionalmente uma implementação SIMD? 2024-02-17 06:17:14 +0800 CST

Hot tag

python javascript c++ c# java typescript sql reactjs html

Explore

  • Início
  • Perguntas
    • Recentes
    • Highest score
  • tag
  • help

Footer

AskOverflow.Dev

About Us

  • About Us
  • Contact Us

Legal Stuff

  • Privacy Policy

Language

  • Pt
  • Server
  • Unix

© 2023 AskOverflow.DEV All Rights Reserve