8
\$\begingroup\$

I am in a fantasy football league and each week I send out a power rankings poll through Microsoft Forms. People will send in their rankings poll and I will create a graphic based on responses. I was doing this manually and decided that a Python script could do this for me instead. Below is the code for my program (there is a fair amount of it):

def Main():
    #Loading Packages
    import pandas as pd
    from datetime import date, timedelta
    import os
    from tkinter.filedialog import askopenfilename
    import rpy2.robjects as robjects
    from PIL import Image as image
    from PIL import ImageDraw as draw
    from PIL import ImageFont
        
    
    '''Creating Working & Final CSV'''
    #Creating working sheet (ws) to perform calculations and data transformation on and creating CSV
    ws = pd.DataFrame()
    ws.to_csv('.\workingSheet.csv')
    #Using placeholder data to populate columns for final sheet (fs) that will be sent to rStudio
    startingdata = [[0,0,0,0,0,0,0,0,0,0],[0,0,0,0,0,0,0,0,0,0],[0,0,0,0,0,0,0,0,0,0],[0,0,0,0,0,0,0,0,0,0],
                    [0,0,0,0,0,0,0,0,0,0],[0,0,0,0,0,0,0,0,0,0],[0,0,0,0,0,0,0,0,0,0],[0,0,0,0,0,0,0,0,0,0],
                    [0,0,0,0,0,0,0,0,0,0],[0,0,0,0,0,0,0,0,0,0]]
    fs = pd.DataFrame(startingdata, index=['Madison Moonshiners (Brady)','Frisco Fireballs (Ethan W.)',
                                           'Margaronas (Tyler)','Colorado Gin Enthusiast (Joel)','Karolina Keg Stands (JC)',
                                           'Dysdunctional Frunks (Jordan)','McCaffeine Free Beverages (McKenna)',
                                           'Team rep292 (Ryan)','Pearl Pilsners (Alex)','Team ethanjflynn (Ethan F.)'],
                  columns=['1st Place Votes','2nd Place Votes','3rd Place Votes','4th Place Votes','5th Place Votes',
                           '6th Place Votes','7th Place Votes','8th Place Votes','9th Place Votes', '10th Place Votes'])
    fs.index.name = 'Team Name'
    fs.to_csv('.\\finalSheet.csv')   #Creating a CSV from dataframe
    
    
    r'''
    This automatically sets the NFL Week, commenting out for testing purposes, replacing with enter week input
    
    #Setting NFL week based on current date
    today = date.today()
    #Setting NFL Week 1 information
    week=1
    testdatestart = date(2022, 9, 14)   #Starts on Wednesdays, which is when power rankings are announced
    testdateend = date(2022, 9, 20)   #Ends on Tuesdays, the day before power rankings announcement
    #Looping through each NFL Week to check what week we are on (slightly adjusted from NFL schedule)
    while week <= 17:   #Only 17 weeks in fantasy football season
        if today >= testdatestart and today <= testdateend:
            break   #The correct date and week was found
        elif today >= date(2023, 1, 11):
            raise Exception('Out of Season')
            break   #Fantasy football season ends early January
        else:      
            week+=1 #If todays date was not within the test date range, add 1 week to NFL Week, and test times
            testdatestart = testdatestart + timedelta(weeks=1)
            testdateend = testdateend + timedelta(weeks=1)
    '''   
        
    week = input('Please enter the NFL Week: ')
    
    r'''
    This automatically pulls the correct file from the downloads folder, where the excel sheet will be sent to. 
    Commenting it out and replacing with choose file option for sake of reproducing on another machine
    
    #Opening Power Rankings (PR) Excel File
    week = str(week)   #Must be string to concatenate
    for (root,dirs,files) in os.walk(r"C:\Users\Jordan Ramsey\Downloads", topdown=True):   
        for name in files:  #Looking through each file in "Downloads" folder, where the power rankings are sent
            if "Week "+week+" Power" in name:   #Selecting the correct file based on week
                break_both = True   #Adding a flag to break from both loops
                break    
        if break_both:   #Breaking from second for loop
            break
    '''
    filename = askopenfilename()
    rs = pd.read_excel(filename)   #Opening correct power rankings file
   
    
    '''Individual polls come back in a string format, seperated by ";". Splitting each vote into its own cell'''
    responses = len(rs.loc[:,'Week '+week+' Power Rankings'])   #Number of responses
    i = 0   #Using this variable to track and limit loops and find row number
    while i <= responses-1:   #Subtracting by 1 due to 0-based indexing
        pr = rs.at[i,'Week '+week+' Power Rankings']   #Grabbing individual poll
        pr = pr.split(';')   #Spliting each response into respective cells
        pr.pop(10)   #Each poll ended with a ";", which created an extra list item. Popping out extra, empty item
        r = 0   #Tracking and limiting loops
        while r <= 9:   #Setting each name into respective cells. Each row is a new poll, each column is a new vote type
            ws.loc[i-1,r] = pr[r]
            r+=1
        i += 1


    '''Assigning point values to rankings and setting up tables for each vote level'''
    b=10   #This variable is the point multiplier for votes
    for a in range(10):   #Looping through each row on every column to find who got votes within each vote level (1st, 2nd, 3rd, etc.)
        add = ws[a].value_counts().rename_axis('team').reset_index(name=str(a)+'points')
#Counting how many times each name appears in each vote level
        count = len(add)   #Counting how many distinct teams in each vote level
        add[str(a)+'points'] *= b   #Multiplying each occurance of a vote by its specific vote level
        b-=1   #Each loop is a new vote level, decreasing vote multiplier
        names = ['Madison Moonshiners (Brady)','Frisco Fireballs (Ethan W.)','Margaronas (Tyler)',
                 'Colorado Gin Enthusiast (Joel)','Karolina Keg Stands (JC)','Dysdunctional Frunks (Jordan)',
                 'McCaffeine Free Beverages (McKenna)','Team rep292 (Ryan)','Pearl Pilsners (Alex)',
                 'Team ethanjflynn (Ethan F.)']   #Listing all league members to sort through with
        i=count   #Location marker to adjust which row to place data on for following loop
        for x in names:   #Looping through names list to check if team is already in the counted table
            if x in add.loc[:,'team']:   #if so, move onto next team and add to location marker
                i+=1
                continue
            else:   #if not, add team to table and add to location marker
                add.loc[i,'team']=x
                add.loc[i,str(a)+'points'] = 0
                i+=1
        add.to_csv('.\PR '+str(a)+r' Place Points.csv', index=False)   #Creating a new CSV for each vote level, that will be merged
    
        
    '''Merging Tables'''    
    b = 1   #Loop tracker and limiter
    for a in range(9):   #Each loop joins a vote level table with the preceeding vote level table (1st joins with 2nd, which joins with 3rd, etc)
        lfile = pd.read_csv('.\PR '+str(a)+' Place Points.csv')
        rfile = pd.read_csv('.\PR '+str(b)+' Place Points.csv')
        points = pd.merge(left = lfile, right = rfile, how = 'outer', left_on = 'team', right_on = 'team',
                          suffixes=('', '_drop')).filter(regex='^(?!.*_drop)')
        #Using an outer merge here due to how I format later on. Merge created duplicates and outer merge at least put the rows that I needed in predictible spots
        points.to_csv('.\PR '+str(b)+' Place Points.csv', index=False)   #Sending each new table back to CSV
        b+=1


    '''Removing Duplicates'''
    teams = list(points.iloc[:,0])   #Creating a list of all teams in table, including duplicates, to remove unnecesary rows
    teamsplus = list(points.iloc[:,0])   #Making a copy of list that will not have rows removed for the sake of iterating through while keeping the same index
    count = len(points)   #Number of total rows
    i = 1   #Starting at 1 because the first row of each new team is the row I need
    j=1   #Second tracker to ensure the rows I need will not be dropped
    for a in teamsplus:   #Iterating through list of team names in the order that they are in within the table. Using 2 lists in the loop because the list will re-index with each pop command
        occur = teams.count(a)   #Finding how many times a given name is duplicated in the table
        if i == count:   #Breaking loop when tracker gets past final row
            break
        elif occur > 1:   #If the team name is in the table more than once, this drops the 2nd occurance of that name (1st occurance is the one I need)
            points.drop(i, inplace=True, axis=0)
            teams.pop(j)   #Must use "j" here because list re-indexes with each pop command
            i+=1
        elif occur == 1:   #If the team name only occurs once, move on to the next name
            i+=1
            j+=1   #"j" will ensure it not only moves to the next team name, but also that the second occurance is the one that will be dropped

   
    '''We now have the correct point values assigned to the correct teams'''


    '''Formatting Tables'''
    fs = pd.read_csv(r'.\finalSheet.csv')
    points.sort_values(['team'], axis=0, ascending=[False], inplace=True, ignore_index=True)   #Ordering table based on team name 

    points.reset_index(drop=True,inplace=True)   #Replacing inaccurate index from removal of duplicates
    fs.sort_values(['Team Name'], axis=0, ascending=[False], inplace=True) #Doing the same with the final sheet so it matches with the points dataframe
    fs.reset_index(drop=True,inplace=True)


    '''Moving Calculated Data to Final Table'''
    data = points.iloc[0:10,1:12]   #Selecting points that the needed data is contained
    fs.iloc[0:10,1:12] = data   #Placing that data into the "fs" dataframe
    points.to_csv('.\PR 9 Place Points.csv', index=False)   #Finished with the points dataframe, sending back to CSV


    '''Adding a total points column and creating list & table for final rankings calculation'''
    i=0
    while i <= 9:   #Looping through each row to add the sum to new column
        fs.loc[i,12] = sum(fs.iloc[i,1:11])
        i+=1        
    fs.rename(columns={12:'Total Points'}, inplace=True)
    fs.sort_values(['Total Points'], axis=0, ascending=False, inplace=True)   #Putting table in ranking order
    rankingsordered = list(fs.iloc[:,0])   #Creating a list that is the team names
    rankingsordered.sort()   #Sorting that list alphabetically
    add = ws.iloc[:,0].value_counts().rename_axis('team').reset_index(name='votes')
#Creating table that shows 1st place votes per team
    add.sort_values(['team'], axis=0, ascending=True, inplace=True)   #Sorting that table alphabetically
 
    
    '''Giving teams who received 1st place votes an indicator of how many - will use later on final graphic'''
    z=0
    withvotes = list()   #Blank list to add onto
    for x in rankingsordered:   #Looping through each team name
        if x in list(add.loc[:,'team']):   #If their name is in the 1st place votes table
            y = str(add.loc[z,'team'])+' ('+str(add.loc[z,'votes'])+')'   #Add an indicator of how many votes they got
            withvotes.append(y)   #And add them to the list
            z+=1
        else:
            withvotes.append(x)   #If they did not receive a 1st place vote, add to list without indicator
     
    
    '''Ordering previously created list in ranking order & sending completed table to CSV'''
    withvotes.sort()   #Sorting alphabetically to match with total points
    fs.sort_values(['Team Name'], axis=0, ascending=[True], inplace=True)   #Sorting table alphabetically by team name to match list  
    fs.to_csv('.\\finalSheet.csv', index=False)   #Sending final rankings table to CSV to be used by rStudio to create graph
    pointsdata = pd.DataFrame(withvotes, columns=['team'])   #Combining team names including 1st place votes with total points
    pointsdata.loc[:,0] = list(fs.loc[:,'Total Points'])   #Adding total points (this was put in the same order as the team names earlier)
    pointsdata.sort_values([0], axis=0, ascending=[False], inplace=True)   #Putting in ranking order
    rankings = list(pointsdata.loc[:,'team'])


    '''Data is correctly formatted and can now by sent to rStudio for creating the stacked bar chart'''  
    r = robjects.r
    r.source('.\graph_creation.R')   #R is better for creating and formatting graphs/charts, all other code is contained within Python


    '''Opening the graph from rStudio along with the base graphic to place graph and rankings on'''
    graph = image.open('.\graph.png')
    graphic = image.open('.\graphic.png')


    '''Resizing & pasting graph onto graphic'''
    newsize = (1300,944)
    graph = graph.resize(newsize)
    graphic.paste(graph,(805,265))   #Exact placement for graph on graphic


    '''Adding Text to Image'''
    gtext = draw.Draw(graphic)
    titleFont = ImageFont.truetype(".\\URW Grotesk Regular.ttf", 108)   #Choosing font style & size for both title and rankings
    rankFont = ImageFont.truetype(".\\URW Grotesk Regular.ttf", 30)
    gtext.text((22,50), "Designated Drinker Week "+week+" Power Rankings", font=titleFont, fill=(255,255,255))   #Placing title on graphic
    x=116   #"x" and "y" are coordinates for first place team name on graphic
    y=273
    a=1
    for z in rankings:
        if a == 10:
            gtext.text((x+20,y), z, font=rankFont, fill=(255,255,255))
            a+=1
        else:
            gtext.text((x,y), z, font=rankFont, fill=(255,255,255))
            y+=98
            a+=1
            
    
    '''Displaying & Saving Image'''
    graphic.show()
    graphic.save(".\GraphicFinal.png")    
    graphic.save(r"C:\Users\Jordan Ramsey\iCloudDrive\Personal\Fantasy Football\Power Rankings\W"+week+" Graphic.png") #Need to change or delete this for the file to run correctly. This line sends the graphic to my phone

    
    '''For use next season, when full automation is set up'''
    #Calling browser script    
    #exec(open("C:\\Users\\Jordan Ramsey\\iCloudDrive\\Personal\\Fantasy Football\\Python Excel Files\\AutoBrowser.py").read())


Main()

About halfway through the python code, it calls an R script to create the graph:

install.packages("ggplot2")
library(ggplot2)
install.packages('tidyr')
library(tidyr)
install.packages('RColorBrewer')
library(RColorBrewer)
install.packages('ggrepel')
library(ggrepel)

#Setting working directory. To have this work on a different machine, switch this to that machines marking directory filepath
setwd("C:\\Users\\Jordan Ramsey\\Documents\\PR Project Files")
getwd()

#Opening Files
points = read.csv(file = "./finalSheet.csv", sep=',')

#Renaming columns for proper indexing
i=1
x=2
while (i <=9)
{
  colnames(points)[x] = paste('X0',i,'_Place_Votes', sep='')
  i=i+1
  x=x+1
}
colnames(points)[11] = 'X10_Place_Votes'

#Creating Plot
pointslong = pivot_longer(points, X01_Place_Votes:X10_Place_Votes, names_to='VoteType', values_to='points')
graph = ggplot(data=pointslong, aes(x=reorder(Team.Name, -Total.Points), y=points, fill=VoteType)) +
  geom_bar(position='stack',stat='identity',width=.5)

#Formatting Plot
graph = graph + labs(x='Team Name') + scale_fill_discrete(name='Vote Type', labels=c('1st Place Votes','2nd Place Votes','3rd Place Votes','4th Place Votes','5th Place Votes','6th Place Votes','7th Place Votes','8th Place Votes','9th Place Votes','10th Place Votes'))
graph = graph + theme(panel.background = element_rect(fill='#353332'), plot.background = element_rect(fill='#353332'))
graph = graph + theme(panel.grid.major = element_blank(), panel.grid.minor.y = element_blank())
graph = graph + theme(legend.background = element_rect(fill='#353332'), legend.key = element_rect(fill='#353332'))
graph = graph + theme(legend.position = 'top')
graph = graph + theme(axis.text.x = element_text(size=10,color='white'), axis.title.x = element_text(size=20,color='white'),axis.text.y = element_text(size=20,color='white'), axis.title.y = element_text(size=20,color='white'), legend.text = element_text(color='white'))
graph = graph + theme(legend.key.size = unit(2, 'cm'),
                      legend.key.height = unit(2, 'cm'),
                      legend.key.width = unit(2, 'cm'),
                      legend.text = element_text(size=20),
                      legend.title = element_text(size=20,color='white'))

#Saving plot and exporting to computer
png(file="./graph.png", width=1712, height=1001)
print(graph)
dev.off()

Here is what the end result looks like: Power Rankings Graphic

Really I would just like for anyone to look through the code and give any advice/tips/better ways to do things. I'm new to Python, so I'm sure this is a jumbled mess, but it performs the way I expect it to, so that is a win in my book. I am applying for data analytics jobs soon, so I plan to include this in my portfolio to show off my ability to use Python. Any help is greatly appreciated! Even if you are only able to check a chunk of the code, that would be very helpful!

This is my first time posting on this site, so please let me know if I did anything wrong with this post or if I forgot to include anything.

\$\endgroup\$

3 Answers 3

5
\$\begingroup\$

Overall very impressive.

The bar labels on the bottom are far too small to be practically legible. You already have a legend: why not just number the entries on the bottom by legend?

There's not a good reason to call into R. You should stick to Python for these purposes (or, alternatively, you could use pure R).

Don't capitalize Main - it's a function, not a class.

You need to break up your main function into multiple subroutines, and move imports out to global scope at the top of the file.

Wherever your CSV is coming from, the fact that it doesn't have the team name index and column names is a shortcoming. This really should not be hard-coded into your code. If necessary, move the team names into their own CSV; but that shouldn't be necessary.

Drop .\\ from your filenames. Among other reasons, it would unnecessarily pin your code to Windows.

If you have large blocks of code commented out "for test purposes",

  1. Move them to a non-commented function, and
  2. Comment out the call to that function.

That way you can still parse the code for basic validity. Use source control, and if this function is never going to be called, just delete it altogether.

This loop:

i = 0   #Using this variable to track and limit loops and find row number
    while i <= responses-1

fails to "loop like a native"; instead

for i in range(responses):

Replace code like this:

'.\PR '+str(b)+' Place Points.csv'

with string interpolation:

f'PR {b} Place Points.csv'

Not a great idea to render to a png. It doesn't scale, it's not searchable, and it's not responsive. Strongly consider rendering to HTML instead, calling into a JavaScript library to do the graph render (or embedding an SVG graph in the HTML).

\$\endgroup\$
3
  • 2
    \$\begingroup\$ I think SVG is the best target, rather than requiring users to enable JavaScript for your site (and perhaps for a library's site, too). \$\endgroup\$ Commented Dec 12, 2022 at 9:20
  • 1
    \$\begingroup\$ Thank you a ton for your compliment and advice. I plan to work on everything you've mentioned over the coming days, your help is greatly appreciated! Quick question: I switched to R because I originally tried making the graph on Python, but couldn't seem to figure out the formatting, such as the darker background, and adjusting the legend. Is this level of customization available in Python and if so, what package(s) should I be looking to use? \$\endgroup\$ Commented Dec 12, 2022 at 18:08
  • 2
    \$\begingroup\$ Start with matplotlib, which is the most popular, well-known and well-documented. If you can't get it to do what you want I would be very surprised. \$\endgroup\$
    – Reinderien
    Commented Dec 12, 2022 at 21:44
4
\$\begingroup\$

I will focus my comments on the R code.

Package Management

Installing packages every time the script runs is not recommended. In addition to increasing run time, you also expose yourself to unanticipated changes in future updates of these packages.

My first recommendation would be to eliminate all calls to install.packages() in this script, and optionally handle package management in a "deployment" scripts.

If you really wanted to auto-install anytime packages aren't available, you can use the available.packages() function and the ability to install multiple packages with a call to install.packages() to only install what needs to be installed as follows. Also, if you have multiple cores, using the Ncpus argument can speed up the installation process by parallelizing.

Missing <- setdiff(c("ggplot2",
                     "tidyr",
                     "RColorBrewer",
                     "ggrepel"),rownames(installed.packages()))
install.packages(Missing,
                 Ncpus = 1,
                 dependencies = c("Depends", "Imports"))

Paths and Interactive Output

As mentioned by others, avoiding In the lines below, the call to getwd() might have helped you out in troubleshooting this originally, but this doesn't do anything useful when this is executed as a part of your automated workflow. Once you have finished troubleshooting, remove code like this from your final script.

#Setting working directory. To have this work on a different machine, switch this to that machines marking directory filepath
setwd("C:\\Users\\Jordan Ramsey\\Documents\\PR Project Files")
getwd()

Looping and column names

The following code could be replaced with a much briefer vectorized version.

#Renaming columns for proper indexing
i=1
x=2
while (i <=9)
{
  colnames(points)[x] = paste('X0',i,'_Place_Votes', sep='')
  i=i+1
  x=x+1
}
colnames(points)[11] = 'X10_Place_Votes'

An approach might be something like as follows. Also,the paste0() function is the same as paste(), but with fixed sep='', and you can use sprintf to pad your single digit numbers with a leading zero.

colnames(points)[2:11] <- paste0('X',sprintf("%02d", seq_len(10)),'_Place_Votes')

Number Suffixes

The scales package provides a handy function to generate numbers with print suffixes that could simplify your scale definition and make this code more flexible in the future for allowing any arbitrary number of labels.

This code

scale_fill_discrete(name='Vote Type', labels=c('1st Place Votes','2nd Place Votes','3rd Place Votes','4th Place Votes','5th Place Votes','6th Place Votes','7th Place Votes','8th Place Votes','9th Place Votes','10th Place Votes'))

can be replaced with the following, and since ggplot2 depends on the scales package already, it doesn't add any additional dependencies to be installed.

scale_fill_discrete(name='Vote Type',
                    labels=paste0(scales::ordinal(seq_len(10))," Place Votes"))

Repeated assignment of intermediate steps for the plot

While this isn't a particularly memory intensive operation, unnecessarily re-assigning objects in R can come at a cost to performance. Since ggplot2 is designed to allow 'piping' all your graph steps together and including all the formatting in a single call to theme(), I'd rewrite your graphing steps as follows:

graph = ggplot(data=pointslong,
               aes(x=reorder(Team.Name, -Total.Points),
                   y=points,
                   fill=VoteType)) +
  geom_bar(position='stack',stat='identity',width=.5) +
  labs(x='Team Name') +
  scale_fill_discrete(name='Vote Type',
                      paste0(scales::ordinal(seq_len(10))," Place Votes")) +
  theme(panel.background = element_rect(fill='#353332'),
        plot.background = element_rect(fill='#353332'),
        panel.grid.major = element_blank(),
        panel.grid.minor.y = element_blank(),
        axis.text.x = element_text(size=10,color='white'),
        axis.title.x = element_text(size=20,color='white'),
        axis.text.y = element_text(size=20,color='white'),
        axis.title.y = element_text(size=20,color='white'),
        legend.text = element_text(size=20,color='white'),
        legend.background = element_rect(fill='#353332'),
        legend.key = element_rect(fill='#353332'),
        legend.position = 'top',
        legend.key.size = unit(2, 'cm'),
        legend.key.height = unit(2, 'cm'),
        legend.key.width = unit(2, 'cm'),
        legend.title = element_text(size=20,color='white')
  )
\$\endgroup\$
1
  • 1
    \$\begingroup\$ Just a minor nitpick: on your re-implementation, you have the same inconsistent spacing and quotes. That's something you could fix, or completely ignore, but, I've upvoted your review anyway. \$\endgroup\$ Commented Dec 14, 2022 at 19:09
2
\$\begingroup\$

"Overall very impressive." <-- I strongly agree with @Reinderien.

It is an impressive piece of code.

And I agree that you should write this in R or Python, instead of both.
But, if you're comfortable having both separated, that's fine too.


And now, to the review!

Python

This will focus only in the Python aspect of your project.

1 - General review

The code seems to be well organized, however, you're making a salad with docstrings and comments.

A docstring isn't a comment, making for a weird reading experience.

Additionally, you mix single and double quotes everywhere.
Please stick to one style, and use it through the entire code.

2 - A script? A module?

Your code seems to be a confusing mesh between a "module" and a script.

You have a function with all the import statements that you use, and you call it right away.

However, you're asking for input inside it.

So, what is it?

If it is supposed to be used as both, you might want to consider using a name guard:

if __name__ == "__main__":
    main()

However ...

3 - Input

You take input somewhere in the middle, without any validation at all.
You also do the same for the excel file.

Have you considered receiving that week as a parameter for the function?

This way, you can have the following:

if __name__ == "__main__":
    while True:
        try:
            week = int(input("Please enter the NFL Week: "))
        except ValueError:
            print("Sorry, I didn't understand that.")
            continue
        else:
            break
    
    filename = askopenfilename()
    
    main(week, excel_filename)

This was adapted from: https://stackoverflow.com/questions/23294658/asking-the-user-for-input-until-they-give-a-valid-response

If you want, you can even just use the argparse library, to receive the input as an argument, and fallback to the solution above to request input, if it doesn't exist.

In fact, you should just make this whole thing into a module and use any/all of these alternatives in a different file, which you use for calling the script.

4 - imports

You have all the imports inside your function.

This is good, but ... if you want to, for example, generate 5-6 graphs, it would re-import everything every single time you run that function.

Please, consider moving everything to the top, since you will use all the imported libraries, at least once.

Additionally, don't rename things like "pandas" to "pd".
And please...

5 - Variable/library names

Please, take care with the names you pick.

You have:

  • ps
  • ws
  • rs
  • r
  • b
  • a
  • lfile
  • rfile
  • x
  • z
  • r

All these names make little sense, and are used for multiple things.

Please use more descriptive names.
In fact, with the point already referred in @Reinderien's answer, you will probably get rid of many variables.

Also, a comment DOES NOT replace good variable names.

6 - Saving the final file

You save the file twice, into 2 different locations.

How about you receive additional export locations as arguments, and save all those extra files?

And instead of re-drawing the graph multiple times, you can, alternatively, just copy the file multiple times.


R

This will focus entirely in the R aspect of your project.

For a better R review, please read @MattSummersgill's answer.

1 - General review

The code is a bit messy, with really weird indentation.

You also do the same single and double quote salad.
Please stick to one style, and use it through the entire code.

You also have inconsistent spacing.
Consider the following lines:

# Inconsistent spacing and quotes
points = read.csv(file = "./finalSheet.csv", sep=',')

# ...

# Inconsistent spacing
  colnames(points)[x] = paste('X0',i,'_Place_Votes', sep='')

# ...

# Weird indentation and inconsistent spacing
  geom_bar(position='stack',stat='identity',width=.5)

These lines have all the "sins" demonstrated.

Additionally, you have really long lines, and really strange formatting on some.
Please consider this example:

# Original code:
graph = graph + theme(axis.text.x = element_text(size=10,color='white'), axis.title.x = element_text(size=20,color='white'),axis.text.y = element_text(size=20,color='white'), axis.title.y = element_text(size=20,color='white'), legend.text = element_text(color='white'))

# New version:
graph = graph + theme(
    axis.text.x = element_text(size=10, color='white'),
    axis.title.x = element_text(size=20, color='white'),
    axis.text.y = element_text(size=20, color='white'),
    axis.title.y = element_text(size=20, color='white'),
    legend.text = element_text(color='white')
)

2 - Paths

Since you're exporting everything using a different language, you can pass the path as an argument to the R program.

This way, you don't have to hard-code the paths, and everything is easier to change in the future.

Remember, you're using 2 programs in 2 languages, and you need to keep them synchronized in terms of changes.

3 - Strange loops

You have the following code:

#Renaming columns for proper indexing
i=1
x=2
while (i <=9)
{
  colnames(points)[x] = paste('X0',i,'_Place_Votes', sep='')
  i=i+1
  x=x+1
}
colnames(points)[11] = 'X10_Place_Votes'

Instead of this weird while, try the for loop:

install.packages("stringr") # <-- if necessary
library(stringr)

#Renaming columns for proper indexing
for (i in 1:10)
{
    colnames(points)[i + 1] = paste("X", str_pad(i, 2, "left", "0"), "_Place_Votes", sep="")
}

This loop should generate the following strings:

"X01_Place_Votes"
"X02_Place_Votes"
"X03_Place_Votes"
"X04_Place_Votes"
"X05_Place_Votes"
"X06_Place_Votes"
"X07_Place_Votes"
"X08_Place_Votes"
"X09_Place_Votes"
"X10_Place_Votes"

The difference between the old x variable was that it started at 2, while i started at 1.
This is a +1 difference from i, therefore using i + 1 for the indexing, eliminating the need for the x variable.


Disclaimer

ALL THE CODE IS WRITTEN WITHOUT BEING 100% TESTED!

Please, assume that it is just pseudo-code, instead of a final implementation.

Use good judgement when taking any code from this review.

Due to learning Python by myself, and never having used R in my life, you should take everything written with a grain of salt.

All style decisions (formatting and what-not) are made based on what is easier to read and write for me.
I did try to follow my interpretation of what I understood to be the standard conventions.

Any questions, please comment below.

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

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