Currently the pto tables contain varying identifiers for one invention title that is involved in a reassignment. The purpose of the table is to have each identifier for an invention title listed in a single row, making it easy to track the invention through various transactions. The Match By column will inform the user which identifier should be used to match to other tables in the patent database.
==Data Cleanup Progress==
The tables that currently comprise the assignment data are as follows.
This section explains the series of steps that were taken to clean and to take note of problems in the data. Additionally, this section includes the codes for new tables that combine patent properties from different tables in the original assignment data.
===Table Cleanup===
The five original assignment tables contain duplicates that were deleted. Five new tables were made and used as templates for building new tables.
Ptoassignmentnd Table:
SELECT COUNT(*) FROM ptoassignment;
--8676322
DROP ptoassignmentnd;
CREATE TABLE ptoassignmentnd AS
SELECT reelno, frameno, max(last_update_date) as last_update_date, purge_indicator, recorded_date, correspondent_name,
SELECT DISTINCT reel_no, frame_no, action_key_code, uspto_transaction_date, uspto_date_produced, version FROM ptopatentfile;
--7159725
===Patent Number Cleanup===
The goal is to only have assignment records on utility patents. The patents in ptoproperty include alphanumerics , which represent reissue and design patents as well as mistakes in the data input. Additionally, the documentids include application numbers or ids and publication numbers. The ptoproperty table stores the patent ids as character strings.
First the duplicates were dropped from the ptoproperty table creating ptoproperty_cleaned.
SELECT COUNT(*) FROM ptoproperty WHERE documentid LIKE 'D%';
--1128247
Finally, all letters were removed from the data, resulting in the final version of ptoproperty_cleaned.
The ptoproperty_cleaned table contains application numbers, publication numbers, and patent numbers. The patents may also have two distinct publication numbers based on the year in which it was published. Based on length of documentid, the three types of id numbers were separated into three separate tables.
DROP TABLE ptoproperty_patent;
CREATE TABLE ptoproperty_patent AS
SELECT * FROM ptoproperty_cleaned WHERE length(documentid) = 7;
--8696149
ALTER TABLE ptoproperty_patent RENAME COLUMN documentid TO patentno;
DROP TABLE ptoproperty_app;
CREATE TABLE ptoproperty_app AS
SELECT * FROM ptopropertynd WHERE length(documentid) = 8;
--11577028
ALTER TABLE ptoproperty_app RENAME COLUMN documentid TO appno;
DROP TABLE ptoproperty_pub;
CREATE TABLE ptoproperty_pub AS
SELECT * FROM ptopropertynd WHERE length(documentid) = 11;
--6217864
ALTER TABLE ptoproperty_pub RENAME COLUMN documentid TO pubno;
All three tables contain the following information and differ only on the type of patent id.
The purpose of the ptotracking tables is to track the ownership of patents based on the update date and filing dates of the assignment. These tables can be used to add missing information or track further refined tables such as ptoproperty_patent.
Ptotracking takes the reelno, frameno, and documentid key from the ptoproperty_cleaned table and joins the update dates and recorded dates corresponding to the transactions.
WHERE (M1.reelno = M2.reelno) AND (M1.frameno = M2.frameno);
--8699074
In making the ptotracking table, it is important to note that the classification of documentids as B1 and B2 causes duplicates in the entries. B1 and B2 classifications mean that the patent was granted with and without a published application.
Ptotracking2 adds the assignee to the transaction, allowing the user to track ownership of the entity and of the patent.
WHERE (M1.reelno = M2.reelno) AND (M1.frameno = M2.frameno);
--9613927
The document ids in the PTO assignment data had not yet been verified as matching to the main patent table in our database (psql patent). The document ids in the PTO assignment data are stored as character strings whereas the patents in the patent table are stored as integers. Unlike the ptoprpoperty_cleaned table, all patent numbers in the patent table are unique.
The following two tables were made in order to verify that the documentids in the ptoproperty_cleaned table match to the patent table.
DROP TABLE edcheck;
CREATE TABLE edcheck AS
SELECT CAST (documentid AS INT) FROM ptotracking2;
SELECT COUNT(DISTINCT documentid) FROM edcheck;
--2343765
DROP TABLE edcheck2;
CREATE TABLE edcheck2 AS
SELECT M1.documentid, M2.patent
FROM edcheck M1, patent M2
WHERE (M1.documentid = M2.patent);
--2238305
DROP TABLE edcheck;
CREATE TABLE edcheck AS
SELECT DISTINCT documentid FROM ptotracking2;
--2343765
DROP TABLE edcheck2;
CREATE TABLE edcheck2 AS
SELECT CAST(documentid AS INT)FROM edcheck;
--2343765
DROP TABLE edcheck3;
CREATE TABLE edcheck3 AS
SELECT M1.documentid, M2.patent FROM edcheck2 M1, patent M2 WHERE M1.documentid = M2.patent;
--2238305
Based on the iterations of these tables, we could conclude that our original patent data forms the majority of the patents undergoing reassignments or transactions.
===US ONLY Patent Assignee Table===
Note: Table made for Julia by [[Marcela Interiano]]
Table made in the patent database using the USPTO assignment data.
The first step was to include the last_update_date in with the data from the ptoproperty table. The ptoproperty table contains only the filing date, which is not useful as we are looking for the current patent holders. The table ptoproperty_patent was used for the patent numbers as this table was cleaned to include only patent numbers, no application or publication numbers.
FROM ptoproperty_patent_minupdate M1, ptoassigneend_us_cleaned M2
WHERE (M1.reelno = M2.reelno) AND (M1.frameno = M2.frameno);
The table below was made to join through using Sonia's zip codes for the ptoassignee data to get patent numbers from reelno and frameno.
DROP TABLE ptoassignee_us_distinct;
CREATE TABLE ptoassignee_us_distinct AS
SELECT DISTINCT reelno, frameno, patentno
FROM ptoassignee_us_patent
GROUP BY reelno, frameno, patentno;
--5391413
The total number of distinct patent numbers in the ptoassignee data for only US assignees is 2345763.
SELECT COUNT(*) FROM (SELECT DISTINCT patentno FROM ptoassignee_us_patent) AS P;
--2345763
===Current Assignee using Recorded Date===
Each assignment has three dates: filingdate, recorded_date, last_update_date. The filingdate corresponds to the filing of the assignment with the USPTO. The recorded_date is the date the transaction was recorded. The last_update_date is the date the USPTO verifies that the assignment still holds. In the ptoassignee_us_patent table, the last_update_date is used to find the current assignee.
Prior to Sonia's work with the ptoassignee table address data, the table ptoassignee_current was made using the most recent recorded_date. This method though is flawed given that additional transactions could have current previously that are still in effect as patents can have multiple assignees. These codes can be used for constructing similar tables using the address data Sonia has cleaned in the following sections of this project.
To begin with, the ptoproperty_patent table was cleaned to drop all duplicates. Then the table was matched with the assignee table.
WHERE (M1.reelno = M2.reelno) AND (M1.frameno = M2.frameno);
--9634942
Once all the location and address fields from the ptoassignee table have been added to the ptoproperty_patent fields, the max recorded_date was identified from the ptoassignee_patent table and from ptoassigneev2 for comparison.
DROP TABLE datecheck;
CREATE TABLE datecheck AS
SELECT documentid, max(recorded_date) as recorded_date FROM ptoassignee_patent GROUP BY documentid;
--2343765
DROP TABLE datecheck;
CREATE TABLE datecheck AS
SELECT documentid, max(recorded_date) as recorded_date FROM ptoassigneev2 GROUP BY documentid;
FROM ptotracking2 M1, ptoassigneend M2 WHERE (M1.reelno = M2.reelno) AND (M1.frameno = M2.frameno);
--16581236
DROP TABLE ptoassignee_current;
CREATE TABLE ptoassignee_current AS
SELECT M1.reelno, M1.frameno, M2.documentid, M2.recorded_date FROM ptoassignee_patent M1, datecheck M2
WHERE (M1.documentid = M2.documentid)
AND (M1.recorded_date = M2.recorded_date);
--6729698
A final version of the ptoassignee_current table was made using ptoassigneev2 given the larger pool of documentids included in the table by matching using documentid and recorded dates from datecheck.
*Five features (addrline1, addrline2, city, state, postcode) in the table contain address information.
*Features addrline1, addrline2 and city are not cleaned. They can have suite, street, city, state and postcode information, or any combination of these
*The first object of this project is to extract postcode, state and city information from the three features above (Section 2.2.2).
*Then, we summarize the postcode, state and city information in the original table and those extracted from addresses to generate only one postcode, state and city for each record (Section 2.2.3).
*For now, we only focus on American patents.
===Extract Address Information===
So far these instructions apply to:
*Records identified as US (by zip code or country feature) ~3.5m records
*Those that well-formatted zip code (5-4) in addrline1, addrline2, and city (not postcode, etc) ~5,000 records
To get the record identified as US, we make the table:
*'''ptoassigneend_allus'''
This table contains all the U.S. patents extracted from ptoassigneend table. The rule to generate this table is 'country = 'UNITED STATES' ' if any of the features contains U.S. postcode.
Noise exists because of postcode errors.
The ptoassigneend_allus table may miss some U.S. patents which lose postcode records.
SQL code:
CREATE TABLE ptoassigneend_allus AS
SELECT * FROM ptoassigneend
WHERE city ~* '.*\d{5}[-]\d{4}.*' OR addrline1 ~* '.*\d{5}[-]\d{4}.*' OR
addrline2 ~* '.*\d{5}[-]\d{4}.*' OR postcode ~* '.*\d{5}[-]\d{4}.*' OR
country ~* 'UNITED STATES';
SELECT 3572682
Table "public.ptoassigneend_allus"
Column | Type | Modifiers
----------| -----------------------|-----------
reelno | integer |
frameno | integer |
name | character varying(500) |
addrline1 | character varying(500) |
addrline2 | character varying(500) |
city | character varying(500) |
state | character varying(500) |
country | character varying(500) |
postcode | character varying(80) |
====Postcode====
U.S. postcode should follow the pattern five digits - four digits. In this way, U.S. patents can be extracted by searching for postcode with regular expression.
Note: It is too risky to extract just 5 digit postcodes as these might street numbers, or other countries postcodes.
THEN regexp_replace(SUBSTRING(city, '\w{2}\s{1,}\d{5}[-]\d{4}'), '\d{5}-\d{4}', '')
Examples:
NEW YORK NY 10022-3201 |NY
WAUKEGAN IL 60085-2195 |IL
'''c. 'D.C.' '''
'D[.]C[.]\s\d{5}-\d{4}'
SQL code:
WHEN city ~* 'D[.]C[.]\s\d{5}-\d{4}'
THEN 'D.C.'
'''d. 'A CORP.* OF State''''
This pattern is not reliable. When 'addrline1' looks this way, 'addrline2' always provide more detailed address information than 'addrline1'. Besides, a great part of state info extracted from 'A CORP.* OF [State]' doesn't match the state extracted from detailed 'addrline2'. In this way, we discard this pattern.
The following patterns can be used to extract city information.
'''a. '\s{2,} CityName [,] State Postcode''''
SQL code:
CASE WHEN addrline1 ~* '\s{2,}\w{1,}\s{0,}\w{0,}\s{0,}[,]\s{0,}\w{1,}\s{0,}\w{0,}\s{0,}\d{5}[-]\d{4}'
THEN LTRIM(RTRIM(REPLACE(SUBSTRING(SUBSTRING(addrline1, '\s{2,}\w{1,}\s{0,}\w{0,}\s{0,}[,]\s{0,}\w{1,}\s{0,}\w{0,}\s{0,}\d{5} [-]\d{4}'),'.*[,]'),',','')))
Examples (in any of addrline1, addrline2, and city):
800 CHRYSLER DR. EAST AUBURN HILLS, MICHIGAN 48326-2757 |AUBURN HILLS
550 MADISON AVENUE NEW YORK, NEW YORK 10022-3201 |NEW YORK
P.O. BOX 15439 WILMINGTON, DE 19850-5439 |WILMINGTON
''Some noise exists (just a little).''
1313 N. MARKET STREET HERCULES PLAZAWILMINGTON, DE 19894-0001
'''b. '[,]\s{1,} CityName [,] State Postcode' '''
SQL code:
CASE WHEN addrline1 ~* '\s{2,}\w{1,}\s{0,}\w{0,}\s{0,}[,]\s{0,}\w{1,}\s{0,}\w{0,}\s{0,}\d{5}[-]\d{4}'
THEN LTRIM(RTRIM(REPLACE(SUBSTRING(SUBSTRING(addrline1, '\s{2,}\w{1,}\s{0,}\w{0,}\s{0,}[,]\s{0,}\w{1,}\s{0,}\w{0,}\s{0,}\d{5}[-]\d{4}'),'.*[,]'),',','')))
Contain all the original features as well as city, state and postcode info extracted from features addrline1, addrline2 and city. See Section 2.2.1 for details.
As mentioned in Section 2.2.2, city, state and postcode info are extracted from 'addrline1', 'addrline2' and 'city'. Original table also contains 'postcode', 'city' and 'state'. In this way, we have four candidates for city, state and postcode.
Info extracted from addresses and that in the original table are not necessarily consistent.
'''The object of this section is to pick out the best postcode, city and state for each record, and create a Master Table with original features and cleaned postcode, city, and state.'''
====Postcode====
Reminder: 'postcode_city' is the postcodes extracted from 'city'; 'postcode_addr1' is the postcodes extracted from 'addrline1'; 'postcode_addr2' is the postcodes extracted from 'addrline2'.
The 'postcode_city', 'postcode_addr1' and 'postcode_addr2' are all consistent!
Examples:
postcode_addr1 | postcode_addr2
---------------|---------------
90401-1708 | 90401-1708
37831-8243 | 37831-8243
The issue is the inconsistency between 'postcode' and ('postcode_city', 'postcode_addr1' and 'postcode_addr2').
* Inconsistency between 'postcode' and 'postcode_addr1'
Case 1: 'postcode_addr1' beats 'postcode' because 'addrline1' is detailed. For example:
For records of which 'addrline1', 'addrline2' and 'city' don't contain postcode info, just clean the 'postcode' as the 'postcode_cleaned'
All the cleaned postcodes for U.S. patents are stored in ptoassigneend_us_cleaned (see feature postcode_cleaned).
====State====
Reminder: 'state_city' is the states extracted from 'city'; 'state_addr1' is the states extracted from 'addrline1'; 'state_addr2' is the states extracted from 'addrline2'.
The 'state_city', 'state_addr1' and 'state_addr2' are consistent.
Examples:
state_addr2 | state_city
------------|-----------
ILLINOIS | ILLINOIS
IL | IL
IL | IL
CT | CT
MN | MN
CA | CA
One reason for the inconsistency between 'state' and ('state_city', 'state_addr1' and 'state_addr2') is the co-existence of abbreviation and full form. So they are the same actually. Otherwise, 'state_city', 'state_addr1' and 'state_addr2' beat 'state' because they are detailed.
For records of which 'addrline1', 'addrline2' and 'city' don't contain state info, just take 'state' as the 'state_cleaned'.
All the cleaned states for U.S. patents are stored in ptoassigneend_us_cleaned (see feature state_cleaned).
Note: We might want to convert state names to standard codes.
====City====
Reminder: 'city_city' is the city info extracted from 'city'; 'city_addr1' is the city info extracted from 'addrline1'; 'city_addr2' is the city info extracted from 'addrline2'.
The 'city_city', 'city_addr1' and 'city_addr2' are consistent.
Examples:
city_addr2 | city_city
-------------|--------------
BOISE | BOISE
THOMASVILLE | THOMASVILLE
CARROLLTON | CARROLLTON
CINCINNATI | CINCINNATI
CINCINNATI | CINCINNATI
PEORIA | PEORIA
OAK RIDGE | OAK RIDGE
CARROLLTON | CARROLLTON
'city_city', 'city_addr1' and 'city_addr2' beat 'city' because they are detailed.
For records of which 'addrline1', 'addrline2' and 'city' don't contain city info, just keep 'city' as the 'city_cleaned'.
Feature postcode_cleaned, postcode_f5_cleaned (first five digits), state_cleaned and city_cleaned are cleaned postcode, state and city info.
===Functions to Simplify SQL Code===
==== Extraction ====
* Postcode
SQL function:
CREATE OR REPLACE FUNCTION ExtractPostcode(adr text) RETURNS text AS $$
BEGIN
RETURN SUBSTRING(adr, '\d{5}[-]\d{4}');
END;
$$ LANGUAGE plpgsql;
Explanation:
adr is the feature from which you want to extract postcode.
* State
SQL function:
CREATE OR REPLACE FUNCTION ExtractState(adr text) RETURNS text AS $$
SELECT
CASE WHEN adr ~* '([,]|[.])\s\w{2,}\s{0,}\w{0,}\s{1,}\d{5}[-]\d{4}'
THEN LTRIM(RTRIM(replace(regexp_replace(SUBSTRING(adr, '[,]\s\w{2,}\s{0,}\w{0,}\s{1,}\d{5}[-]\d{4}'), '\d{5}-\d{4}', ''),',','')))
WHEN adr ~* '(^|\s)\w{2}\s{1}\d{5}[-]\d{4}'
THEN regexp_replace(SUBSTRING(adr, '\w{2}\s{1,}\d{5}[-]\d{4}'), '\d{5}-\d{4}', '')
WHEN adr ~* 'D[.]C[.]\s\d{5}-\d{4}'
THEN 'D.C.'
ELSE NULL END AS result;
$$ LANGUAGE SQL;
Explanation:
adr is the feature from which you want to extract state.
* City
SQL function:
CREATE OR REPLACE FUNCTION ExtractCity(adr text) RETURNS text AS $$
SELECT
CASE WHEN adr ~* '\s{2,}\w{1,}\s{0,}\w{0,}\s{0,}[,]\s{0,}\w{1,}\s{0,}\w{0,}\s{0,}\d{5}[-]\d{4}'
THEN LTRIM(RTRIM(REPLACE(SUBSTRING(SUBSTRING(adr, '\s{2,}\w{1,}\s{0,}\w{0,}\s{0,}[,]\s{0,}\w{1,}\s{0,}\w{0,}\s{0,}\d{5}[-]\d{4}'),'.*[,]'),',','')))
WHEN adr ~* '[,]\s{0,}\w{1,}\s{0,}\w{0,}\s{0,}[,]\s{0,}\w{1,}\s{0,}\w{0,}\s{0,}\d{5}[-]\d{4}'
THEN LTRIM(RTRIM(REPLACE(SUBSTRING(SUBSTRING(adr, '[,]\s{0,}\w{1,}\s{0,}\w{0,}\s{0,}[,]\s{0,}\w{1,}\s{0,}\w{0,}\s{0,}\d{5}[-]\d{4}'),'[,].*[,]'),',','')))
WHEN adr ~* '^\w{1,}\s{0,}\w{0,}\s{0,}[,]\s{0,}\w{1,}\s{0,}\w{0,}\s{0,}\d{5}[-]\d{4}'
THEN REPLACE(SUBSTRING(SUBSTRING(adr, '^\w{1,}\s{0,}\w{0,}\s{0,}[,]\s{0,}\w{1,}\s{0,}\w{0,}\s{0,}\d{5}[-]\d{4}'), '.*[,]'),',', '')
WHEN adr ~* '^\w{1,}\s{0,}\w{0,}\s{0,}\w{2}\s{0,}\d{5}[-]\d{4}'
THEN regexp_replace(SUBSTRING(adr, '^\w{1,}\s{0,1}\w{0,}\s{1,}\w{2}\s{0,}\d{5}[-]\d{4}'), '\w{2}\s\d{5}[-]\d{4}', '')
WHEN adr ~* 'BATON[,] ROUGE[,]\s{1}LA'
THEN 'BATON ROUGE'
WHEN adr ~* 'ST[.]\s{1}PAUL'
THEN 'ST. PAUL'
WHEN adr ~* 'ARLINGTON[,]\s{1}VA'
THEN 'ARLINGTON'
ELSE NULL END AS result;
$$ LANGUAGE SQL;
Explanation:
adr is the feature from which you want to extract city.
With these functions, we can generate ptoassigneend_us_extracted, which is the final output in Section 2.2.1, with the following code:
*Remove city, state, zip, country from addrline1 & addrline2 to get clean addrlines.
*Maybe concatenate addrline1 and addrline to make addrline
*Identify clean data (e.g. City that is a city, zip that is a zip, state that is a state)
**By pattern, length, match to list
*Try some more patterns, perhaps with a slightly higher false positive rate, on the remaining uncleaned data
**Iterate!
=== Identify Clean Data ===
==== Output: ptoassigneend_us_identify0 ====
As mentioned in Section 3, the ptoassigneend_us_extracted is clean. Copy all the records in ptoassigneend_us_extracted to ptoassigneend_us_identify0.
Store remaining records in ptoassigneend_us_temp.
==== Output: ptoassigneend_us_identify1 ====
The following section works on ptoassigneend_us_temp.
Filter out records with city that is a city, zip that is a zip, state that is a state.
Note: The consistency between city and state or city and postcode is not checked in this section.
* ptoassigneend_us_citylist
Copy clean city records in ptoassigneend_us_extracted to ptoassigneend_us_citylist (775).
Since the city list is not long, I briefly cleaned the list by hand, and updated the ptoassigneend_us_citylist (730).
* zip that is a zip
Match the pattern 5d-4d or 5 digits.
*state that is a state
Select distinct state records with
SELECT DISTINCT state FROM ptoassigneend_us_temp;
The output shows that all the records not null or not spaces are valid state names.
*city that is a city
One method to identify clean city is to find city records that match ptoassigneend_us_citylist.
SQL Code:
SELECT city
FROM ptoassigneend_us_temp
WHERE city IN (
SELECT citylist
FROM ptoassigneend_us_citylist);
-- SELECT 2603422
* The table ptoassigneend_us_identify1 stores records that meet all the requirements above: zip with 5-4 or 5 digits, state not null and not spaces, and city in ptoasigneend_us_citylist.
SQL Code:
CREATE TABLE ptoassigneend_us_identify1 AS
SELECT *
FROM ptoassigneend_us_temp
WHERE city IN (
SELECT citylist
FROM ptoassigneend_us_citylist) AND
state IS NOT NULL AND state != '' AND
postcode ~* '\d{5}';
SELECT 2511356
Store remaining records in ptoassigneend_us_temp2.
==== Output: ptoassigneend_us_identify2 ====
Part of 'city' contains comma at the end. Remove comma, and then match 'city' with ptoassigneend_us_citylist.
SELECT *, replace(city, ',', '') clean_city
FROM ptoassigneend_us_temp2;
# SELECT 1055874
The output is ptoassigneend_us_identify2.
SQL code:
CREATE TABLE ptoassigneend_us_identify2 AS
SELECT *
FROM ptoassigneend_us_temp3
WHERE clean_city IN (
SELECT citylist
FROM ptoassigneend_us_citylist) AND
state IS NOT NULL AND state != '' AND
postcode ~* '\d{5}';
# SELECT 14508
Store remaining data (excluding data in ptoassigneend_us_identify0, ptoassigneend_us_identify1 & ptoassigneend_us_identify2) in ptoassigneend_us_temp3.
=== Clean Address: more patterns ===
Table: ptoassigneend_us_temp3
SQL code is in E:\McNair\Projects\PatentAddress\RestructureAddressInfo(Second Stage).sql
====Clean Postcode====
Identifying five-digit postcode is risky because of the existence of P.O. BOX #, SUITE #, etc.
One option is to identify state and postcode together with the following SQL function:
CREATE OR REPLACE FUNCTION ExtractPostcode2(adr text) RETURNS text AS $$
SELECT CASE WHEN (adr ~* '([,]|[.])\s\w{2,}\s{0,}\w{0,}\s{1,}\d{5}' OR
After extracting postcode, the following function is used to get clean postcode.
The priority is 'postcode' if it is '\d{5}', postcode_addr1, postcode_addr2, postcode_city
CREATE OR REPLACE FUNCTION PostcodeClean2 (text,text,text,text) RETURNS text AS $$
$pri1=$_[0];
$pri2=$_[1];
$pri3=$_[2];
$postcode=$_[3];
if ($postcode) {return $postcode;}
if ($pri1) {return $pri1;}
if ($pri2) {return $pri2;}
if ($pri3) {return $pri3;}
return undef;
$$ LANGUAGE plperl;
The details and SQL function are in E:\McNair\Projects\PatentAddress\RestructureAddressInfo(Second Stage).sql
The output is table ptoassigneend_us_postex which include a new feature 'postcode_extracted'.
====Clean 'city'====
'city' is cleaned using following patterns.
*Pattern 1: 'city' ~ 'city name, state ID'
*Pattern 2: 'city' ~ 'city name, state postcode (5 digits)'
*Pattern 3: 'city' ~ 'city name,'
The SQL function is:
CREATE OR REPLACE FUNCTION ExtractCity2(adr text) RETURNS text AS $$
SELECT
CASE WHEN adr ~* '[,]\s{1,}\w{2}(\s{1,}|$|[.]|[,])' OR
adr ~* '[,]\s{1}\w{1}[.]\w{1}[.]'
THEN REPLACE(SUBSTRING(adr, '.*[,]'),',','')
WHEN adr ~* '[,].*\d{5}$'
THEN REPLACE(SUBSTRING(adr, '.*[,]'),',','')
WHEN adr ~* '.*[,]$'
THEN REPLACE(adr, ',', '')
ELSE adr END AS result;
$$ LANGUAGE SQL;
The details and SQL function are in E:\McNair\Projects\PatentAddress\RestructureAddressInfo(Second Stage).sql
The output is table ptoassigneend_us_postex2 which include a new feature 'city_extracted' and 'postcode_extracted'.
CREATE TABLE ptoassigneend_us_postex2 AS
SELECT *, ExtractCity2(city) city_extracted
FROM ptoassigneend_us_postex;
===Identify Clean Data (Round Two)===
A new list of clean city is extracted in Section 4.3.2. This list, combined with 'ptoassigneend_us_citylist', creates a new city list 'ptoassigneend_us_citylist2' which can be used to identify clean data.
Since the city list is not long, I briefly cleaned it by hand, and stored it in ptoassigneend_us_citylist2.
* Actually, we can find a full list of U.S. cities online: https://www.uscitieslist.org/.
====Output: ptoassigneend_us_identify3====
Similar to Section 4.2, identify clean data that meets all the requirements: postcode_extracted with 5-4 or 5 digits, state not null and not spaces, and city_extracted in ptoasigneend_us_citylist2.
Union ptoassigneend_us_identify(0-4) to generate ptoassigneend_us_identify_subtotal with clean city, state and postcode. This table contains 89.5% of all the records in ptoassigneend_allus. 10.5% left in ptoassigneend_us_temp5.
One problem of records in ptoassigneend_us_temp5 is that the postcode is missing.
ptoassigneend_us_candid1 is a subset of ptoassigneend_us_temp5. It contains clean city and state info, but postcode is missing.
SQL code:
CREATE TABLE ptoassigneend_us_candid1 AS
SELECT *
FROM ptoassigneend_us_temp5
WHERE city_extracted2 IN (
SELECT citylist
FROM ptoassigneend_us_citylist2) AND
state IS NOT NULL AND state != '';
SELECT 136958
Remaining records are in table ptoassigneend_us_temp6 (SELECT 239837).
====Output: ptoassigneend_us_candid2====
ptoassigneend_us_candid2 is also a subset of ptoassigneend_us_temp5. It contains clean postcode info, but city and state are not identified.
I randomly checked the city_extracted in ptoassigneend_us_candid2, and it is quite clean. Some city records are misspelt, such as 'Oklahama City'. We may identify clean city based on the length of records.
Note: About 60 records are missing. For example, the # of records in ptoassigneend_us_temp + # of records in ptoassigneend_us_identify0 != # ptoassigneend_allus.
====To do====
* Remove city, state, zip, country from addrline1 & addrline2 to get clean addrlines.
* Maybe concatenate addrline1 and addrline to make addrline