How to use cartopy to create colored US states - cartopy

I need to create a map where states have different colors depending on a piece of data about that state. I found an example of a US map in the cartopy gallery, but it didn't demonstrate how to refer to the states and access their attributes, and there little else out there:
From the example, I've simplified their code to the following, and would appreciate any help with modifying this to get the face colors of the states to be set according to the magnitude of popdensity for the state.
import matplotlib.pyplot as plt
import cartopy.crs as ccrs
import cartopy.io.shapereader as shpreader
fig = plt.figure()
ax = fig.add_axes([0, 0, 1, 1], projection=ccrs.LambertConformal())
ax.set_extent([-125, -66.5, 20, 50], ccrs.Geodetic())
shapename = 'admin_1_states_provinces_lakes_shp'
states_shp = shpreader.natural_earth(resolution='110m',
category='cultural', name=shapename)
popdensity = {
'New Jersey': 438.00,
'Rhode Island': 387.35,
'Massachusetts': 312.68,
'Connecticut': 271.40,
'Maryland': 209.23,
'New York': 155.18,
'Delaware': 154.87,
'Florida': 114.43,
'Ohio': 107.05,
'Pennsylvania': 105.80,
'Illinois': 86.27,
'California': 83.85,
'Virginia': 69.03,
'Michigan': 67.55,
'Indiana': 65.46,
'North Carolina': 63.80,
'Georgia': 54.59,
'Tennessee': 53.29,
'New Hampshire': 53.20,
'South Carolina': 51.45,
'Louisiana': 39.61,
'Kentucky': 39.28,
'Wisconsin': 38.13,
'Washington': 34.20,
'Alabama': 33.84,
'Missouri': 31.36,
'Texas': 30.75,
'West Virginia': 29.00,
'Vermont': 25.41,
'Minnesota': 23.86,
'Mississippi': 23.42,
'Iowa': 20.22,
'Arkansas': 19.82,
'Oklahoma': 19.40,
'Arizona': 17.43,
'Colorado': 16.01,
'Maine': 15.95,
'Oregon': 13.76,
'Kansas': 12.69,
'Utah': 10.50,
'Nebraska': 8.60,
'Nevada': 7.03,
'Idaho': 6.04,
'New Mexico': 5.79,
'South Dakota': 3.84,
'North Dakota': 3.59,
'Montana': 2.39,
'Wyoming': 1.96}
ax.background_patch.set_visible(False)
ax.outline_patch.set_visible(False)
ax.set_title('State Population Density')
for state in shpreader.Reader(states_shp).geometries():
### I need to replace the following code with code that sets the
### facecolor as a gradient based on the population density above
facecolor = [0.9375, 0.9375, 0.859375]
edgecolor = 'black'
ax.add_geometries([state], ccrs.PlateCarree(),
facecolor=facecolor, edgecolor=edgecolor)
plt.show()

To have access to states' attributes, you need to iterate through .records() rather than .geometries(). Here is a working code based on yours. Read comments in the code's portions that I add / modified for clarification.
import matplotlib.pyplot as plt
import cartopy.crs as ccrs
import cartopy.io.shapereader as shpreader
fig = plt.figure()
ax = fig.add_axes([0, 0, 1, 1], projection=ccrs.LambertConformal())
ax.set_extent([-125, -66.5, 20, 50], ccrs.Geodetic())
shapename = 'admin_1_states_provinces_lakes_shp'
states_shp = shpreader.natural_earth(resolution='110m',
category='cultural', name=shapename)
popdensity = {
'New Jersey': 438.00,
'Rhode Island': 387.35,
'Massachusetts': 312.68,
'Connecticut': 271.40,
'Maryland': 209.23,
'New York': 155.18,
'Delaware': 154.87,
'Florida': 114.43,
'Ohio': 107.05,
'Pennsylvania': 105.80,
'Illinois': 86.27,
'California': 83.85,
'Virginia': 69.03,
'Michigan': 67.55,
'Indiana': 65.46,
'North Carolina': 63.80,
'Georgia': 54.59,
'Tennessee': 53.29,
'New Hampshire': 53.20,
'South Carolina': 51.45,
'Louisiana': 39.61,
'Kentucky': 39.28,
'Wisconsin': 38.13,
'Washington': 34.20,
'Alabama': 33.84,
'Missouri': 31.36,
'Texas': 30.75,
'West Virginia': 29.00,
'Vermont': 25.41,
'Minnesota': 23.86,
'Mississippi': 23.42,
'Iowa': 20.22,
'Arkansas': 19.82,
'Oklahoma': 19.40,
'Arizona': 17.43,
'Colorado': 16.01,
'Maine': 15.95,
'Oregon': 13.76,
'Kansas': 12.69,
'Utah': 10.50,
'Nebraska': 8.60,
'Nevada': 7.03,
'Idaho': 6.04,
'New Mexico': 5.79,
'South Dakota': 3.84,
'North Dakota': 3.59,
'Montana': 2.39,
'Wyoming': 1.96}
ax.background_patch.set_visible(False)
ax.outline_patch.set_visible(False)
ax.set_title('State Population Density')
#for state in shpreader.Reader(states_shp).geometries():
for astate in shpreader.Reader(states_shp).records():
### You want to replace the following code with code that sets the
### facecolor as a gradient based on the population density above
#facecolor = [0.9375, 0.9375, 0.859375]
edgecolor = 'black'
try:
# use the name of this state to get pop_density
state_dens = popdensity[ astate.attributes['name'] ]
except:
state_dens = 0
# simple scheme to assign color to each state
if state_dens < 40:
facecolor = "lightyellow"
elif state_dens > 200:
facecolor = "red"
else:
facecolor = "pink"
# `astate.geometry` is the polygon to plot
ax.add_geometries([astate.geometry], ccrs.PlateCarree(),
facecolor=facecolor, edgecolor=edgecolor)
plt.show()
The resulting plot:

Related

Population by United States Region

Using SQL Let’s say we have a dataset of population by state (e.g., Vermont, 623,251, and so on), but we want to know the population by United States region (e.g., Midwest, 68,985,454). Could you describe how you would go about doing that?
Dataset from census.gov
Where I'm stuck at
--First I created a table with a state and population column.
CREATE TABLE states (
state VARCHAR(20),
population INT
);
--Then I uploaded a CSV file from census.gov that I cleaned up.
SELECT * FROM states;
--Created a temporary table to add in the region column.
DROP TABLE IF EXISTS temp_Regions;
CREATE TEMP TABLE temp_Regions (
state VARCHAR(20),
state_pop INT,
region VARCHAR(20),
region_pop INT
);
INSERT INTO temp_Regions
SELECT state, population
FROM states;
--Used CASE WHEN statements to put states in to their respective regions.
SELECT state,
CASE WHEN state IN ('Connecticut', 'Maine', 'Massachusetts', 'New Hampshire', 'Rhode Island', 'Vermont', 'New Jersey', 'New York', 'Pennsylvania') THEN 'Northeast'
WHEN state IN ('Illinois', 'Indiana', 'Michigan', 'Ohio', 'Wisconsin', 'Iowa', 'Kansas', 'Minnesota', 'Missouri', 'Nebraska', 'North Dakota', 'South Dakota') THEN 'Midwest'
WHEN state IN ('Delaware', 'District of Columbia', 'Florida', 'Georgia', 'Maryland', 'North Carolina', 'South Carolina', 'Virginia', 'West Virginia', 'Alabama', 'Kentucky', 'Mississippi', 'Tennessee', 'Arkansas', 'Louisiana', 'Oklahoma', 'Texas') THEN 'South'
WHEN state IN ('Arizona', 'Colorado', 'Idaho', 'Montana', 'Nevada', 'New Mexico', 'Utah', 'Wyoming', 'Alaska', 'California', 'Hawaii', 'Oregon', 'Washington') THEN 'West'
END AS region, state_pop, region_pop
FROM temp_Regions;
--Now I'm stuck at this point. I'm unable to get data into the region_pop column. How do I get the sum of the populations by U.S. Region?
Let me know if you need further clarification on things. Thanks for your help y'all!
You can make use of analytical function sum() over(partition by) to achieve this
with data
as (
SELECT state
,CASE WHEN state IN ('Connecticut', 'Maine', 'Massachusetts', 'New Hampshire', 'Rhode Island', 'Vermont', 'New Jersey', 'New York', 'Pennsylvania') THEN 'Northeast'
WHEN state IN ('Illinois', 'Indiana', 'Michigan', 'Ohio', 'Wisconsin', 'Iowa', 'Kansas', 'Minnesota', 'Missouri', 'Nebraska', 'North Dakota', 'South Dakota') THEN 'Midwest'
WHEN state IN ('Delaware', 'District of Columbia', 'Florida', 'Georgia', 'Maryland', 'North Carolina', 'South Carolina', 'Virginia', 'West Virginia', 'Alabama', 'Kentucky', 'Mississippi', 'Tennessee', 'Arkansas', 'Louisiana', 'Oklahoma', 'Texas') THEN 'South'
WHEN state IN ('Arizona', 'Colorado', 'Idaho', 'Montana', 'Nevada', 'New Mexico', 'Utah', 'Wyoming', 'Alaska', 'California', 'Hawaii', 'Oregon', 'Washington') THEN 'West'
END AS region
, state_pop
FROM temp_Regions
)
select state
,region
,state_pop
,sum(state_pop) over(partition by region) as region_population
from data

Get the Pandas groupby agg output columns

Here is my code
import pandas as pd
df = pd.DataFrame()
df['country'] = ['UK', 'UK', 'USA', 'USA', 'USA']
df['name'] = ['United Kingdom', 'United Kingdom', 'United States', 'United States', 'United States']
df['year'] = [1, 2, 1, 2, 3]
df['x'] = [100, 125, 200, 225, 250]
print(df.groupby(['country', 'name']).agg({'x':['mean', 'count']}))
The output I get is
x
mean count
country name
UK United Kingdom 112.5 2
USA United States 225.0 3
But I need a result as a list of rows
[['UK','United Kingdom',112.5,2],...]
or columns
[['UK', 'USA'],['United Kingdom','United States'],[112.5,225],[2,3]]
The name column can consist of an arbitrary number of words, e.g. Kingdom of the Netherlands.
Thank you
Convert MultiIndex to columns by as_index=False parameter, then convert DataFrame to numpy array and last to list:
print(df.groupby(['country', 'name'], as_index=False).agg({'x':['mean', 'count']}).to_numpy().tolist())
[['UK', 'United Kingdom', 112.5, 2], ['USA', 'United States', 225.0, 3]]
For second output add transposing:
print(df.groupby(['country', 'name'], as_index=False).agg({'x':['mean', 'count']}).T.to_numpy().tolist())
[['UK', 'USA'], ['United Kingdom', 'United States'], [112.5, 225.0], [2, 3]]

Plot rectangle using defined x-axis in Matplotlib

I'd like to plot rectangles using defined xticks and ylim using Matplotlib, as shown in the example below:
import matplotlib.pyplot as plt
x = ['00:00', '01:00', '02:00', '03:00', '04:00' , '05:00', '06:00', '07:00', '08:00' ,'09:00' ,'10:00', '11:00', '12:00',
'13:00', '14:00', '15:00', '16:00', '17:00', '18:00', '19:00', '20:00', '21:00', '22:00', '23:00']
plt.ylim([1,10])
With those limits, use the x index to print a rectangle following this:
rect = Rectangle((x[4], x[7]), 4, 8, color='yellow')
Finally, the idea is have multiples rectangles. There's a way to do that without use date/time functions?
The parameters for plt.Rectangle are ((x, y), width, height). You can draw a rectangle for example as follows:
import matplotlib.pyplot as plt
from matplotlib.colors import to_rgba
x = ['00:00', '01:00', '02:00', '03:00', '04:00', '05:00', '06:00', '07:00', '08:00', '09:00', '10:00', '11:00',
'12:00', '13:00', '14:00', '15:00', '16:00', '17:00', '18:00', '19:00', '20:00', '21:00', '22:00', '23:00']
plt.figure(figsize=(15, 5))
plt.xticks(range(len(x)), x)
plt.ylim([1, 10])
x_start, x_end = 4, 7
y_start, y_end = 4, 8
ax = plt.gca()
ax.add_patch(plt.Rectangle((x_start, y_start), x_end - x_start, y_end - y_start,
facecolor=to_rgba('crimson', 0.5), edgecolor='black', lw=2))
plt.show()

Why doesn't the class attribute update when I change the input value? Tkinter Python 3

I am trying to develop a simple application with tkinter and I want to create a class that performs a search in a dataframe, the search's value it is linked to the information typed in an entry that belongs to another class, however when I run the code, the value of the search is not modified when changing the value of the input. Here the code so far
from tkinter import *
import numpy as np
import pandas as pd
cat = {'N°_Cel': pd.Series(['3007441467', '3164992937', '3212400100', '3258172402', '3169995802'], index= [0, 1, 2, 3, 4]),
'Names': pd.Series(['Jairo Moreno', 'Laura Mesa', 'Camilo Baquero', 'Daniela Smith', 'Ximena Sterling'], index= [0, 1, 2, 3, 4]),
'Adress': pd.Series(['Cll 158 # 96A 25','Cll 132 # 100A 63', 'Cll 158 # 100A 63', 'Cll 148 # 97A 41', 'Cll 172A # 8 20'], index= [0, 1, 2, 3, 4]),
'Schedule': pd.Series(['N.A.', 'Evening.', 'N.A.', 'After 14', 'Morning'], index= [0, 1, 2, 3, 4]),
'Payment': pd.Series(['Credit', 'Cash', 'Credit', 'Credit', 'Cash'], index= [0, 1, 2, 3, 4])}
customer_cat = pd.DataFrame(cat)
class Search_Client():
def __init__(self, info, dataframe, colname):
self.info = info
self.dataframe = dataframe
self.colname = colname
def search(self):
if self.info == '':
result = ''
else:
result = self.dataframe.loc[self.dataframe[self.colname]==self.info]
if result.empty == True:
result = ''
else:
results = result
print(results)
class Order_Document():
def __init__(self, parent):
self.parent = parent
self.parent.geometry('')
self.parent.title('Order document')
self.cellphone_number = StringVar()
Label(self.parent, text= 'Order Document', bg= 'light gray', font= 'Verdana 13', relief= RIDGE).grid(row= 0, column= 0, columnspan= 3) # Se debe centrar al finalizar el diseño de la ventana
Label(self.parent, text= 'Cellphone Number: ', bg = 'white smoke', font= 'Arial 11 bold', justify= 'left').grid(row= 5, column= 0, sticky= 'we')
Entry(self.parent, textvariable= self.cellphone_number, font= 'Arial 11').grid(row=5, column= 1, sticky= 'we')
self.search_client = Search_Client(self.cellphone_number.get(), customer_cat, 'N°_Cel')
Button(self.parent, text= 'Buscar', command= self.search_client.search, bg= 'gold', font= 'Verdana 13').grid(row= 5, column= 3, sticky= 'we')
app = Tk()
ejec = Order_Document(app)
app.mainloop()
I don't know what I could be doing wrong, I hope you can help me.

Linking The id of the Automaticlly Genereated Records in Odoo

I have created custom module that automatically create a journal items when user click on a button. In this custom module I have many2one field called x_move_id
x_move_id = fields.Many2one('account.move', string="Journal", readonly=True)
that should show the reference of the created journal items automatically, similar to account.invoice module when user validate a bill, journal items get created and its id appear.
code:
class BillOfEntry(models.Model):
_name = 'entry.bill'
_description = 'Bill of Entry'
name = fields.Char()
state = fields.Selection([
('draft', 'Draft'),
('sent', 'Validate'),
('done', 'Paid'),
('cancel', 'Cancelled'),
], string='BOE Status', readonly=True, copy=False, store=True, default='draft')
date = fields.Date(string="Date")
custom_agent = fields.Many2one('res.partner', string="Custom Agent")
reference = fields.Char(string="Reference")
total_customs = fields.Float(string='Total Customs', store=True, readonly=True, track_visibility='always', digits=(14, 3))
total_tax = fields.Float(string='Total Tax', store=True, readonly=True, track_visibility='always', digits=(14, 3))
inelig_tax = fields.Float(string="Ineligible Tax", store=True, readonly=True, track_visibility='always', digits=(14, 3))
total_amount = fields.Float(string='Total Amount', store=True, readonly=True, track_visibility='always', digits=(14, 3))
entry_line = fields.One2many('entry.bill.line', 'entry_ref', string="Bill of Entry Line")
input_vat = fields.Many2one('account.account', string="Input VAT")
output_vat = fields.Many2one('account.account', string="Output VAT")
customs_account = fields.Many2one('account.account', string="Customs Account")
x_move_id = fields.Many2one('account.move', string="Journal", readonly=True)
def entry_move_line(self):
data_line = []
line = {}
for record in self:
for move in record.entry_line:
tax = move.tax_id.id
data = (0,0, {
'account_id': record.input_vat.id,
'partner_id': record.custom_agent.id,
'name': move.product_ids.name,
'debit': 0,
'credit': 0,
'x_vat_code': move.vat_code_id.id,
'tax_ids': [(6, 0, [tax])],
})
data_line.append(data)
line = {
'name': record.name,
'date': record.date,
'ref': record.reference,
'line_ids': data_line,
'journal_id': 3,
'state': 'posted'
}
record.move_id.create(line)
record.update({
'state': 'sent'
})
class BillOfEntry(models.Model):
_name = 'entry.bill.line'
_description = 'Bill of Entry Line'
assessable_amount = fields.Float('Assessable Amount', digits=(14, 3))
customs_amount = fields.Float('Customs + Additional Cost', digits=(14, 3))
tax_amount = fields.Float('Tax Amount', digits=(14, 3))
taxable_amount = fields.Float('Taxable Amount', digits=(14, 3))
elig_perc = fields.Float(string="ITC Eligibility %", help="Input Tax Credit Eligibility", digits=(14, 3))
vat_code_id = fields.Many2one('vat.configuration', string="VAT Code")
tax_id = fields.Many2many('account.tax', string='Taxes', domain=['|', ('active', '=', False), ('active', '=', True)])
product_ids = fields.Many2one('product.product', string="product")
inelegible = fields.Float(string="Ineleigible", digits=(14, 3))
entry_ref = fields.Many2one('entry.bill', string='Bill of Entry')
So my question how to get the (id) of the created journal items in custom module?
You can write as following :
def entry_move_line(self):
data_line = []
line = {}
for record in self:
for move in record.entry_line:
tax = move.tax_id.id
data = (0,0, {
'account_id': record.input_vat.id,
'partner_id': record.custom_agent.id,
'name': move.product_ids.name,
'debit': 0,
'credit': 0,
'x_vat_code': move.vat_code_id.id,
'tax_ids': [(6, 0, [tax])],
})
data_line.append(data)
line = {
'name': record.name,
'date': record.date,
'ref': record.reference,
'line_ids': data_line,
'journal_id': 3,
'state': 'posted'
}
account_move = self.env['account.move'].create(line)
record.write({'x_move_id':account_move.id})
record.update({
'state': 'sent'
})