Ir para o conteúdo
VivaPython

[Resolvido] Ordenar lista de tuplo

Mensagens Recomendadas

VivaPython

Boa tarde, eu tenho a seguinte questão sobre escrever uma função:

Cada tuplo contém os seguintes campos, e por esta ordem:

  • Nome do corredor,
  • Nome da prova,
  • Distância da prova, em metros,
  • Dia da prova, no formato MM-DD, representando um dia do mês. Por exemplo, 05-06 representa o dia 6 de maio,
  • Calorias gastas.

dados = [('Ana', 'Lisboa', 42195, '10-18', 2224),
('Eva', 'Nova Iorque', 42195, '06-13', 2319),
('Ana', 'Toquio', 42195, '02-22', 2403),
('Eva', 'Sao Paulo', 21098, '04-12', 1182),
('Ana', 'Sao Paulo', 21098, '04-12', 1096),
('Dulce', 'Toquio', 42195, '02-22', 2449),
('Ana', 'Boston', 42195, '04-20', 2187)]

Uma função calorias_acumuladas que, dada uma lista de tuplos como formato descrito acima e o nome de um atleta, devolve uma lista com as calorias acumuladas ao longo do ano. Por exemplo:

>>> calorias_acumuladas(dados, 'Ana')
[2403, 3499, 5686, 7910]

Não tenho ideia de como fazer , ja tentei separa a data nome e calorias tudo em listas separadas mas depois nao sei o que fazer , nao sei se ha uma maneira mais facil , dai a minha questão , obrigado

Editado por pwseo
tópico reformatado...

Partilhar esta mensagem


Ligação para a mensagem
Partilhar noutros sites
VivaPython

Ja , ate ja a usei para ordenar aquela lista que falei so com as datas , agora ordenar a lista principal a " dados " nao consigo

Partilhar esta mensagem


Ligação para a mensagem
Partilhar noutros sites
pwseo

VivaPython,

Só tens que dizer à sorted() como ordenar. Se leres a documentação relevante, verás que tem lá o seguinte:

key specifies a function of one argument that is used to extract a comparison key from each list element: key=str.lower. The default value is None (compare the elements directly).

Trocando isto por miúdos, se passares uma função que extraia o 4º elemento de cada tuplo, então a sorted() irá utilizar isso como termo para ordenação:

def extrair_data(tuplo):
   'Devolve o 4º elemento de um tuplo'
   return tuplo[3]

dados_por_data = sorted(dados, key = extrair_data)

Logicamente, não é necessário criarmos uma função extrair_data, é perfeitamente possível fazer isto sem funções adicionais. No entanto, uma vez que estás a iniciar-te, resolvi mostrar-te todos os passos.

De outra forma, poderíamos simplesmente utilizar:

# utilizar uma função anónima para devolver o 4º elemento
sorted(dados, key = lambda x: x[3])

Partilhar esta mensagem


Ligação para a mensagem
Partilhar noutros sites
VivaPython

É isso mesmo pwseo , obrgiado. Agora a minha questão é como que somamos as calorias cumulativamente e associamos essas calorias á atleta sendo que os parametros da função é a lista e o nome do atleta que pretendemos

Partilhar esta mensagem


Ligação para a mensagem
Partilhar noutros sites
pwseo

VivaPython,

Tens que seleccionar apenas os tuplos correspondentes à pessoa que pretendes... e num ciclo vais somando, guardando os resultados intermédios. Não posso adiantar mais nada sem ver código teu (senão seria como resolver eu directamente).

Partilhar esta mensagem


Ligação para a mensagem
Partilhar noutros sites
VivaPython

Ate agora não tenho muito , esta função está-me realmente a dar muito problemas, já tentei adicionar primeiro as calorias a uma lista quer será a afinal e nem essa parte esta a dar bem. Nem estou a ver como faço a somos nem como faço correspondência ao nome que é um parâmetro da função

datas_ordenadas = sorted(dados, key = lambda x: x[3])
new_list = []
i=0
while i<len(dados) and dados[i][0] == dados[i+1][0]:
    new_list.append(dados[i][4])
i = i + 1    

Editado por pwseo
syntax highlight.

Partilhar esta mensagem


Ligação para a mensagem
Partilhar noutros sites
pwseo

Sabes o que é pseudo-código? Experimenta primeiro escrever pseudo-código com o que pretendes fazer. Depois convertemos isso em Python.

Adenda: nesse pseudo-código, evita pensar em termos de índices de listas e afins. Pensa de uma forma mais geral... Vais ver que é simples. Python é uma linguagem muito expressiva, não precisamos de andar a fazer coisas complicadas com ela.

Partilhar esta mensagem


Ligação para a mensagem
Partilhar noutros sites
pwseo

Tipo isto:

dados = ordenar(dados, por = data)
total = 0
acumulador = []
para cada entrada em dados:
   se o nome for 'Ana':
       total = total + calorias
       acumulador.append(total)

Já não digo mais nada

Partilhar esta mensagem


Ligação para a mensagem
Partilhar noutros sites
VivaPython

consegui fazer isto, no entanto nao deu o que é suposto nao percebo porque :/

datas_ordenadas = sorted(dados, key = lambda x: x[3])

calorias = [elemento[4] for elemento in datas_ordenadas]
nomes = [elemento[0] for elemento in datas_ordenadas]

total = 0
lista_final = []
for i in range(len(datas_ordenadas)-1):
 if nomes[i] == nomes[i+1]:
	 total = total + calorias[i]
	 lista_final.append(total)

Editado por VivaPython

Partilhar esta mensagem


Ligação para a mensagem
Partilhar noutros sites
pwseo

VivaPython,

Isso está muito mais confuso do que precisa ser.

Para que servem as seguintes linhas:

# Para que é o -1 ali?
for i in range(len(datas_ordenadas)-1):

#...

   # e esta condição faz o quê?
   if nomes[i] == nomes[i+1]:

Partilhar esta mensagem


Ligação para a mensagem
Partilhar noutros sites
VivaPython

   if nomes[i] == nomes[i+1]:

Isto fiz para ir buscar as calorias sempre que os nomes de cada tuplo sejam os mesmos , ou seja , seja a mesma atleta , que é isso que é pedido no entanto não está a fazer isso , se fizer print do acumulador vai dar isto :

>>> calorias_acumuladas(dados,'Ana')
[1096]

e deveria dar isto:

>>> calorias_acumuladas(dados, 'Ana')
[2403, 3499, 5686, 7910]

Partilhar esta mensagem


Ligação para a mensagem
Partilhar noutros sites
pwseo

Não faria mais sentido verificar se o nome é 'Ana' (o que eu faria) em vez de verificar se o nome do elemento actual é o mesmo que o nome do elemento seguinte? (o que tu fizeste)

Obviamente, quem diz 'Ana', diz «o segundo argumento da função».

Partilhar esta mensagem


Ligação para a mensagem
Partilhar noutros sites
VivaPython

A questão é que a função não tem de ser para aquela lista especifica , tem de funcionar para o geral , se quando for avaliado , os professores decidirem por outro nome a função não funcionará estas a perceber , eu tenho de fazer isso para qualquer nome, é essa a minha duvida

Editado por VivaPython

Partilhar esta mensagem


Ligação para a mensagem
Partilhar noutros sites
pwseo

A questão é que a função não tem de ser para aquela lista especifica , tem de funcionar para o geral , se quando for avaliado , os professores decidirem por outro nome a função não funcionará estas a perceber , eu tenho de fazer isso para qualquer nome, é essa a minha duvida

Eu percebi tudo isso. Mas a tua solução não funciona, porque depende da existência de duas entradas consecutivas com o mesmo nome; é por esse motivo que te aparece apenas um resultado.

O que tu tens que fazer é verificar se o elemento tem um nome igual ao argumento passado à função.... Sabes o que são argumentos, correcto?

Partilhar esta mensagem


Ligação para a mensagem
Partilhar noutros sites
VivaPython

Sim , o da função é:

>>> calorias_acumuladas(dados, nome)

Em que dados é a lista e o nome corresponde ao nome da atleta. O que tenho de fazer é associar o argumento "nome" aos nomes das atletas que estão nos tuplos , mas é isso que não estou a perceber e basicamente penso que seja só isso que falte

Partilhar esta mensagem


Ligação para a mensagem
Partilhar noutros sites
pwseo

Já experimentaste um simples if nome == nomes? Ou seja, comparar o nome passado como argumento à função com o nome da entrada que está a ser analisada.

Partilhar esta mensagem


Ligação para a mensagem
Partilhar noutros sites
VivaPython

Já , da erro a dizer que o argumento " nome " não está definido e eu meu codigo está assim:

def calorias_acumuladas(dados,nome):

datas_ordenadas = sorted(dados, key = lambda x: x[3])

calorias = [elemento[4] for elemento in datas_ordenadas]
nomes = [elemento[0] for elemento in datas_ordenadas]

total = 0
lista_final = []
for i in range(len(datas_ordenadas)-1):
 if nome == nomes[i]:
	 total = total + calorias[i]
	 lista_final.append(total)
print lista_final

Está-me a faltar alguma coisa para definir o argumento nome.

Editado por VivaPython

Partilhar esta mensagem


Ligação para a mensagem
Partilhar noutros sites
pwseo

VivaPython,

O teu código está mal indentado. Em Python, a indentação é muito importante, caso contrário, obtemos erros:

~/.../tmp $ python -i vivapython.py 
 File "vivapython.py", line 11
   datas_ordenadas = sorted(dados, key = lambda x: x[3])
                 ^
Indentationerror: expected an indented block

Depois de indentar correctamente o teu código, obtemos o seguinte:

def calorias_acumuladas(dados,nome):

   datas_ordenadas = sorted(dados, key = lambda x: x[3])

   calorias = [elemento[4] for elemento in datas_ordenadas]
   nomes = [elemento[0] for elemento in datas_ordenadas]

   total = 0
   lista_final = []
   for i in range(len(datas_ordenadas)-1):
            if nome == nomes[i]:
                    total = total + calorias[i]
                    lista_final.append(total)
   print lista_final

E quando tentamos testar, acontece isto:

~/.../tmp $ python -i vivapython.py 
>>> calorias_acumuladas(dados, 'Ana')
[2403, 3499, 5686]

Como vês, o código funciona, mas o resultado é ligeiramente diferente do esperado. Está relacionado com uma linha de código que pedi para me explicares mais acima: a linha onde tens um ciclo for.

Partilhar esta mensagem


Ligação para a mensagem
Partilhar noutros sites
VivaPython

Fiz essa linha para poder ter um i para poder is buscar a lista "calorias" e "nome" todos os elementos um a um para depois pode adiciona-los a nova lista. O i corresponde ao index dos elementos dessas listas e achei que fosse mais facil para usar ai, não estou a ver outra maneira de o fazer

Partilhar esta mensagem


Ligação para a mensagem
Partilhar noutros sites
pwseo

Com um pouco de sentido crítico e explorador pensarias o seguinte: «bem, se no resultado final me falta um valor e tenho ali -1 no ciclo... o que acontecerá se eu tirar aquele -1

Eis o que acontece:

~/.../tmp $ python -i vivapython.py 
>>> calorias_acumuladas(dados, 'Ana')
[2403, 3499, 5686, 7910]

Explicação mais longa:

>>> len(dados)
7
>>> range(len(dados))
[0, 1, 2, 3, 4, 5, 6]
>>> range(len(dados) - 1)
[0, 1, 2, 3, 4, 5]

O problema é que ao colocares o -1 o ciclo só vai passar por 6 dos 7 elementos, percebeste?

Nota: isto de fazer ciclos com índices em Python não é bonito. Quando chegares à solução final, mostro uma melhor, mais adequada à linguagem que estás a utilizar.

Partilhar esta mensagem


Ligação para a mensagem
Partilhar noutros sites
VivaPython

Ah é isso mesmo , ja deu correto , muito obrigado. A minha duvida e onde eu estava a tomar mais a atençao quando disseste isso do ciclo for era se era mesmo preciso ter ai o i ou se tirando esse for havia outra maneira mais facil de fazer , nem reparei nesse pequeno detalhe , ja deu sim senhora , muito obrigado mesmo, desculpa lá qualquer coisinha :P

Editado por VivaPython

Partilhar esta mensagem


Ligação para a mensagem
Partilhar noutros sites
pwseo

Ah é isso mesmo , ja deu correto , muito obrigado. A minha duvida e onde eu estava a tomar mais a atençao quando disseste isso do ciclo for era se era mesmo preciso ter ai o i ou se tirando esse for havia outra maneira mais facil de fazer , nem reparei nesse pequeno detalhe , ja deu sim senhora , muito obrigado mesmo, desculpa lá qualquer coisinha :P

Pois, é mesmo isso. Mais tarde coloco aqui uma versão sem o i (entre outras coisas). Em Python há certos detalhes com os quais não precisamos de nos preocupar... e quando preocupamos, muitas vezes perdemos a noção de pequenos pormenores que estão a tramar o raciocínio.

Partilhar esta mensagem


Ligação para a mensagem
Partilhar noutros sites
pwseo

Aqui vai uma solução alternativa:

from operator import itemgetter
from itertools import accumulate

def cumcals(data, name):
   # primeiro excluímos entradas com nomes diferentes
   filtered_data = ( e for e in data if e[0] == name )

   # depois ordenamos por data
   sorted_data = sorted(filtered_data, key = itemgetter(3))

   # e agora devolvemos a soma cumulativa
   return list(accumulate(e[4] for e in sorted_data))

A solução acima só funciona em Python 3, pois a função accumulate() só apareceu lá. Mas podemos implementá-la para Python 2 também:

def accumulate(it):
   total = 0
   for i in it:
       total += i
       yield total

Partilhar esta mensagem


Ligação para a mensagem
Partilhar noutros sites

Crie uma conta ou ligue-se para comentar

Só membros podem comentar

Criar nova conta

Registe para ter uma conta na nossa comunidade. É fácil!

Registar nova conta

Entra

Já tem conta? Inicie sessão aqui.

Entrar Agora

×

Aviso Sobre Cookies

Ao usar este site você aceita os nossos Termos de Uso e Política de Privacidade. Este site usa cookies para disponibilizar funcionalidades personalizadas. Para mais informações visite esta página.