3
\$\begingroup\$

I have posted the below also at stackoverflow in this thread, but a poster there recommended I post this here. Thanks for any suggestions.

I have the below code, and as someone new to Python, I was hoping for suggestions on making it more Pythonic. Posters in this thread have provided very helpful numpy and list comprehension solutions; however, I'd like to improve my more basic nested loop solution solely for the sake of becoming better with basic Python tasks (please do let me know if I should have posted this in the prior thread instead of starting a new semi-duplicate...apologies if that's what I should have done).

Here's the current code (which works as desired):

sales_data = [[201703, 'Bob', 3000], [201703, 'Sarah', 6000], [201703, 
'Jim', 9000], [201704, 'Bob', 8000], [201704, 'Sarah', 7000], [201704, 
'Jim', 12000], [201705, 'Bob', 15000], [201705, 'Sarah', 14000], [201705, 
'Jim', 8000], [201706, 'Bob', 10000], [201706, 'Sarah', 18000]]

sorted_sales_data = sorted(sales_data, key=lambda x: -x[2])
date_list = []
sales_ranks = []

for i in sales_data:
    date_list.append(i[0])

sorted_dates = sorted(set(date_list), reverse=True)

for i in sorted_dates:
    tmp_lst = []
    tmp_lst.append(i)
    for j in sorted_sales_data:
        if j[0] == i:
            tmp_lst.append(j[1])
    sales_ranks.append(tmp_lst)

print(sales_ranks)

Any suggestions on making this more Pythonic (other than numpy or list comprehension solutions) would be greatly appreciated. I am using Python 3.6.

\$\endgroup\$
  • \$\begingroup\$ however, I'd like to improve my more basic nested loop solution solely for the sake of becoming better with basic Python tasks Is there any specific reason why you explicitly want to avoid list comprehensions then? After all, they are the pythonic way of dealing with a lot of basic Python tasks. \$\endgroup\$ – Raimund Krämer Feb 21 '18 at 11:02
  • \$\begingroup\$ I ultimately did use list comprehension for this code, but my goal with this question was just to gain exposure to more ways to do the same task and improve my awareness of other Python functionality. In this case, I learned about itemgetter and groupby. \$\endgroup\$ – CodingNewb Feb 26 '18 at 16:56
3
\$\begingroup\$

There are multiple more Pythonic and more effective (time and space complexity wise) solutions that can be applied to the problem, but if I were to review this code without evaluating it's complexity and would only look for code-style, here is what I would note:

  • i, j and tmp_lst are not descriptive variable names, use better naming
  • you can unpack your sublists into individual variables in a loop, using _ as a throwaway variable name
  • you can use operator.itemgetter() instead of a lambda function:

    from operator import itemgetter 
    
    sorted_sales_data = sorted(sales_data, key=itemgetter(2), reverse=True) 
    
  • you can initialize your inner list with a single item instead of initializing it as an empty list and appending afterwards

Here is the improved "grouping" code block:

for date in sorted_dates:
    date_group = [date]
    for current_date, name, _ in sorted_sales_data:
        if current_date == date:
            date_group.append(name)
    sales_ranks.append(date_group)
\$\endgroup\$
4
\$\begingroup\$

Using a pure Python approach, this is exactly the job for itertools.groupby. But for it to work efficiently, you need to sort your data by the same key you will group them by (i.e. the date). Since your first sort also sort by the third field, you need to sort your list by multiple attributes

I would also build a generator to help reuse code and separate the core of your computation from the testing code using if __name__ == '__main__'. Something along the lines of:

from operator import itemgetter
import itertools


def generate_ranks(sales_data):
    sorted_data = sorted(sales_data, key=itemgetter(0, 2), reverse=True)
    for date, group in itertools.groupby(sorted_data, key=itemgetter(0)):
        yield [date] + [entry[1] for entry in group]


if __name__ == '__main__':
    sales_data = [
        [201703, 'Bob', 3000],
        [201703, 'Sarah', 6000],
        [201703, 'Jim', 9000],
        [201704, 'Bob', 8000],
        [201704, 'Sarah', 7000],
        [201704, 'Jim', 12000],
        [201705, 'Bob', 15000],
        [201705, 'Sarah', 14000],
        [201705, 'Jim', 8000],
        [201706, 'Bob', 10000],
        [201706, 'Sarah', 18000],
    ]
    sales_ranks = list(generate_ranks(sales_data))
    print(sales_ranks)
\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.